189 lines
5.2 KiB
Dart
189 lines
5.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:flutter_application_1/app/app_session_view_model.dart';
|
|
import 'package:flutter_application_1/core/enums/activity_access_state_enum.dart';
|
|
import 'package:flutter_application_1/features/activity/activity_login_gate_page.dart';
|
|
import 'package:flutter_application_1/features/activity/activity_offline_blocked_page.dart';
|
|
import 'package:flutter_application_1/features/activity/activity_page.dart';
|
|
import 'package:flutter_application_1/features/auth/login_page.dart';
|
|
import 'package:flutter_application_1/features/device_connection/pages/connect_device_page.dart';
|
|
import 'package:flutter_application_1/features/home/home_page.dart';
|
|
import 'package:flutter_application_1/shared/dialogs/confirmation_dialog.dart';
|
|
import 'package:flutter_application_1/shared/dialogs/warning_dialog.dart';
|
|
|
|
class AppShell extends StatefulWidget {
|
|
const AppShell({super.key});
|
|
|
|
@override
|
|
State<AppShell> createState() => _AppShellState();
|
|
}
|
|
|
|
class _AppShellState extends State<AppShell> {
|
|
late final PageController _pageController;
|
|
AppSessionViewModel? _session;
|
|
bool _isShowingConnectionLostDialog = false;
|
|
|
|
Future<bool> _confirmExitToLogin() async {
|
|
final session = context.read<AppSessionViewModel>();
|
|
await session.setBlockingDialogVisible(true);
|
|
if (!mounted) {
|
|
return false;
|
|
}
|
|
final shouldExit = await ConfirmationDialog.show(
|
|
context,
|
|
title: 'Keluar aplikasi?',
|
|
message:
|
|
'Jika Anda keluar, koneksi ke skateboard akan diputus dan sesi saat ini akan berakhir.',
|
|
confirmText: 'Keluar',
|
|
cancelText: 'Batal',
|
|
isDestructive: true,
|
|
);
|
|
if (!mounted) {
|
|
return false;
|
|
}
|
|
await session.setBlockingDialogVisible(false);
|
|
|
|
if (!shouldExit || !mounted) {
|
|
return false;
|
|
}
|
|
|
|
await session.disconnectAndEndSession();
|
|
|
|
if (!mounted) {
|
|
return false;
|
|
}
|
|
|
|
Navigator.of(context).pushAndRemoveUntil(
|
|
MaterialPageRoute(builder: (_) => const LoginPage()),
|
|
(route) => false,
|
|
);
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_pageController = PageController(initialPage: 0);
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
if (!mounted) {
|
|
return;
|
|
}
|
|
context.read<AppSessionViewModel>().setControlSurfaceActive(true);
|
|
});
|
|
}
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
|
|
final newSession = context.read<AppSessionViewModel>();
|
|
if (_session == newSession) {
|
|
return;
|
|
}
|
|
|
|
_session?.removeListener(_handleSessionStateChanged);
|
|
_session = newSession;
|
|
_session?.addListener(_handleSessionStateChanged);
|
|
}
|
|
|
|
Future<void> _handleSessionStateChanged() async {
|
|
final session = _session;
|
|
if (!mounted || session == null || _isShowingConnectionLostDialog) {
|
|
return;
|
|
}
|
|
|
|
if (!session.shouldShowConnectionLostDialog) {
|
|
return;
|
|
}
|
|
|
|
_isShowingConnectionLostDialog = true;
|
|
final message = session.connectionLostDialogMessage;
|
|
session.consumeConnectionLostDialog();
|
|
|
|
await session.setBlockingDialogVisible(true);
|
|
if (!mounted) {
|
|
_isShowingConnectionLostDialog = false;
|
|
return;
|
|
}
|
|
await WarningDialog.show(
|
|
context,
|
|
title: 'Koneksi terputus',
|
|
message: message,
|
|
buttonText: 'Ke Connect Device',
|
|
);
|
|
if (!mounted) {
|
|
_isShowingConnectionLostDialog = false;
|
|
return;
|
|
}
|
|
await session.setBlockingDialogVisible(false);
|
|
|
|
if (!mounted) {
|
|
_isShowingConnectionLostDialog = false;
|
|
return;
|
|
}
|
|
|
|
Navigator.of(context).pushAndRemoveUntil(
|
|
MaterialPageRoute(builder: (_) => const ConnectDevicePage()),
|
|
(route) => false,
|
|
);
|
|
|
|
_isShowingConnectionLostDialog = false;
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_session?.removeListener(_handleSessionStateChanged);
|
|
_session?.setControlSurfaceActive(false);
|
|
_pageController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
Widget _buildActivityArea(ActivityAccessState state) {
|
|
switch (state) {
|
|
case ActivityAccessState.unauthenticated:
|
|
return const ActivityLoginGatePage();
|
|
case ActivityAccessState.offlineBlocked:
|
|
return const ActivityOfflineBlockedPage();
|
|
case ActivityAccessState.ready:
|
|
return const ActivityPage();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final session = context.watch<AppSessionViewModel>();
|
|
|
|
return PopScope<Object?>(
|
|
canPop: false,
|
|
onPopInvokedWithResult: (didPop, result) async {
|
|
if (didPop) return;
|
|
|
|
final currentPage = _pageController.hasClients
|
|
? (_pageController.page ?? 0).round()
|
|
: 0;
|
|
|
|
if (currentPage != 0) {
|
|
await _pageController.animateToPage(
|
|
0,
|
|
duration: const Duration(milliseconds: 220),
|
|
curve: Curves.easeOut,
|
|
);
|
|
return;
|
|
}
|
|
|
|
await _confirmExitToLogin();
|
|
},
|
|
child: Scaffold(
|
|
body: PageView(
|
|
controller: _pageController,
|
|
children: [
|
|
HomePage(onExitRequested: _confirmExitToLogin),
|
|
_buildActivityArea(session.activityAccessState),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|