From e261f73c303f63f116ae3406d09b0cc05587bc97 Mon Sep 17 00:00:00 2001 From: Armand <4831c0@proton.me> Date: Sat, 28 Feb 2026 09:03:01 +0100 Subject: [PATCH] switch to go_router --- firka/lib/app/app_state.dart | 8 + firka/lib/app/initialization_screen.dart | 72 +- firka/lib/main.dart | 11 +- firka/lib/routing/app_router.dart | 268 +++++++ firka/lib/routing/shell_with_nav_bar.dart | 118 +++ .../routing/swipable_navigator_container.dart | 92 +++ .../ui/components/common_bottom_sheets.dart | 37 +- firka/lib/ui/phone/pages/extras/extras.dart | 72 +- .../lib/ui/phone/pages/home/home_grades.dart | 9 +- .../phone/pages/home/home_grades_subject.dart | 447 ++++++----- firka/lib/ui/phone/pages/home/home_main.dart | 8 +- .../lib/ui/phone/pages/home/home_subpage.dart | 72 -- .../ui/phone/pages/home/home_timetable.dart | 7 +- .../phone/pages/home/home_timetable_mo.dart | 7 +- .../ui/phone/screens/debug/debug_screen.dart | 13 +- .../ui/phone/screens/home/beta_screen.dart | 9 +- .../ui/phone/screens/home/home_screen.dart | 757 ++---------------- .../screens/settings/settings_screen.dart | 19 +- firka/lib/ui/phone/widgets/login_webview.dart | 12 +- firka/pubspec.yaml | 1 + 20 files changed, 866 insertions(+), 1173 deletions(-) create mode 100644 firka/lib/routing/app_router.dart create mode 100644 firka/lib/routing/shell_with_nav_bar.dart create mode 100644 firka/lib/routing/swipable_navigator_container.dart delete mode 100644 firka/lib/ui/phone/pages/home/home_subpage.dart diff --git a/firka/lib/app/app_state.dart b/firka/lib/app/app_state.dart index 86dcff7..90795ae 100644 --- a/firka/lib/app/app_state.dart +++ b/firka/lib/app/app_state.dart @@ -1,6 +1,7 @@ import 'dart:typed_data'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:firka/api/client/kreta_client.dart'; import 'package:firka/data/models/token_model.dart'; import 'package:firka/core/state/update_notifier.dart'; @@ -17,12 +18,19 @@ final GlobalKey navigatorKey = GlobalKey(); late AppInitialization initData; bool initDone = false; +/// Set when app router is created; used for deep links and notifications. +GoRouter? appRouter; + final dio = Dio(); final isBeta = true; final ValueNotifier isLightMode = ValueNotifier(true); final UpdateNotifier globalUpdate = UpdateNotifier(); +/// Used by home shell screens for pull-to-refresh coordination. +final UpdateNotifier homeUpdateNotifier = UpdateNotifier(); +final UpdateNotifier homeUpdateFinishedNotifier = UpdateNotifier(); + class DeviceInfo { String model; diff --git a/firka/lib/app/initialization_screen.dart b/firka/lib/app/initialization_screen.dart index db93d2f..59284c6 100644 --- a/firka/lib/app/initialization_screen.dart +++ b/firka/lib/app/initialization_screen.dart @@ -6,16 +6,21 @@ import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:firka/app/app_state.dart'; import 'package:firka/app/initialization.dart'; import 'package:firka/core/firka_bundle.dart'; +import 'package:firka/routing/app_router.dart'; import 'package:firka/services/watch_sync_helper.dart'; import 'package:firka/l10n/app_localizations.dart'; -import 'package:firka/ui/phone/screens/debug/debug_screen.dart'; -import 'package:firka/ui/phone/screens/home/home_screen.dart'; -import 'package:firka/ui/phone/screens/login/login_screen.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:go_router/go_router.dart'; -class InitializationScreen extends StatelessWidget { - InitializationScreen({super.key}); +class InitializationScreen extends StatefulWidget { + const InitializationScreen({super.key}); + @override + State createState() => _InitializationScreenState(); +} + +class _InitializationScreenState extends State { + GoRouter? _router; final Future _init = initializeApp().timeout( const Duration(seconds: 20), ); @@ -25,7 +30,6 @@ class InitializationScreen extends StatelessWidget { return FutureBuilder( future: _init, builder: (context, snapshot) { - // Check if initialization is complete if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasError) { logger.shout( @@ -36,16 +40,15 @@ class InitializationScreen extends StatelessWidget { FlutterNativeSplash.remove(); - // Handle initialization error return MaterialApp( - key: ValueKey('errorPage'), + key: const ValueKey('errorPage'), home: DefaultAssetBundle( bundle: FirkaBundle(), child: Scaffold( body: Center( child: Text( 'Error initializing app: ${snapshot.error}', - style: TextStyle(color: Colors.red), + style: const TextStyle(color: Colors.red), ), ), ), @@ -53,9 +56,6 @@ class InitializationScreen extends StatelessWidget { ); } - // Initialization successful, determine which screen to show - Widget screen; - assert(snapshot.data != null); initData = snapshot.data!; initDone = true; @@ -89,43 +89,33 @@ class InitializationScreen extends StatelessWidget { watchChannel.invokeMethod('sendMessageToWatch', { "id": "pong", }); - navigatorKey.currentState?.push( - MaterialPageRoute( - builder: (context) => HomeScreen( - initData, - true, - model: msg["model"] as String? ?? "unknown", - ), - ), - ); + _router?.go('/home'); } } }; } - if (snapshot.data!.tokens.isEmpty) { - screen = LoginScreen(initData, key: ValueKey('loginScreen')); - } else { - screen = HomeScreen(initData, false, key: ValueKey('homeScreen')); + if (_router == null) { + _router = createAppRouter(); + appRouter = _router; } - return MaterialApp( + return MaterialApp.router( title: 'Firka', - key: ValueKey('firkaApp'), - navigatorKey: navigatorKey, + key: const ValueKey('firkaApp'), + routerConfig: _router!, theme: ThemeData( primarySwatch: Colors.lightGreen, visualDensity: VisualDensity.adaptivePlatformDensity, ), - localizationsDelegates: [ + localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], supportedLocales: AppLocalizations.supportedLocales, - home: DefaultAssetBundle( - bundle: FirkaBundle(), - child: ValueListenableBuilder( + builder: (context, child) { + return ValueListenableBuilder( valueListenable: isLightMode, builder: (context, isLight, _) { final overlay = SystemUiOverlayStyle( @@ -143,24 +133,10 @@ class InitializationScreen extends StatelessWidget { return AnnotatedRegion( value: overlay, - child: screen, + child: child ?? const SizedBox.shrink(), ); }, - ), - ), - routes: { - '/login': (context) => DefaultAssetBundle( - bundle: FirkaBundle(), - child: LoginScreen(initData, key: ValueKey('loginScreen')), - ), - '/home': (context) => DefaultAssetBundle( - bundle: FirkaBundle(), - child: HomeScreen(initData, false, key: ValueKey('homeScreen')), - ), - '/debug': (context) => DefaultAssetBundle( - bundle: FirkaBundle(), - child: DebugScreen(initData, key: ValueKey('debugScreen')), - ), + ); }, ); } diff --git a/firka/lib/main.dart b/firka/lib/main.dart index 43496ff..f5d9e1f 100644 --- a/firka/lib/main.dart +++ b/firka/lib/main.dart @@ -6,7 +6,6 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:firka/app/app_state.dart'; import 'package:firka/app/initialization.dart'; import 'package:firka/app/initialization_screen.dart'; -import 'package:firka/ui/phone/pages/error/error_page.dart'; void main() async { logger = Logger("Firka"); @@ -35,14 +34,8 @@ void main() async { logger.shout('Caught error: $error'); logger.shout('Stack trace: $stackTrace'); - navigatorKey.currentState?.push( - MaterialPageRoute( - builder: (context) => ErrorPage( - key: ValueKey('errorPage'), - exception: error.toString(), - ), - ), - ); + final message = '$error\n$stackTrace'; + appRouter?.go('/error', extra: message); }, ); } diff --git a/firka/lib/routing/app_router.dart b/firka/lib/routing/app_router.dart new file mode 100644 index 0000000..c2f56f0 --- /dev/null +++ b/firka/lib/routing/app_router.dart @@ -0,0 +1,268 @@ +import 'dart:collection'; + +import 'package:flutter/material.dart'; +import 'package:firka/core/firka_bundle.dart'; +import 'package:firka/app/app_state.dart'; +import 'package:firka/core/settings.dart'; +import 'package:firka/ui/phone/pages/home/home_grades.dart'; +import 'package:firka/ui/phone/pages/home/home_grades_subject.dart'; +import 'package:firka/ui/phone/pages/home/home_main.dart'; +import 'package:firka/ui/phone/pages/home/home_timetable.dart'; +import 'package:firka/ui/phone/pages/home/home_timetable_mo.dart'; +import 'package:firka/ui/phone/screens/debug/debug_screen.dart'; +import 'package:firka/ui/phone/screens/home/beta_screen.dart'; +import 'package:firka/ui/phone/pages/error/error_page.dart'; +import 'package:firka/ui/phone/screens/login/login_screen.dart'; +import 'package:firka/ui/phone/screens/message/message_screen.dart'; +import 'package:firka/ui/phone/screens/home/home_screen.dart'; +import 'package:firka/ui/phone/screens/settings/settings_screen.dart'; +import 'package:firka/routing/shell_with_nav_bar.dart'; +import 'package:firka/routing/swipable_navigator_container.dart'; +import 'package:go_router/go_router.dart'; + +import 'package:firka/api/model/notice_board.dart'; + +GoRouter createAppRouter() { + return GoRouter( + navigatorKey: navigatorKey, + initialLocation: _initialLocation, + redirect: _redirect, + routes: [ + GoRoute( + path: '/error', + builder: (context, state) { + final exception = state.extra is String + ? state.extra as String + : 'Unknown error'; + return DefaultAssetBundle( + bundle: FirkaBundle(), + child: ErrorPage(key: state.pageKey, exception: exception), + ); + }, + ), + GoRoute( + path: '/login', + builder: (context, state) => DefaultAssetBundle( + bundle: FirkaBundle(), + child: LoginScreen(initData, key: state.pageKey), + ), + ), + GoRoute( + path: '/beta', + builder: (context, state) => DefaultAssetBundle( + bundle: FirkaBundle(), + child: BetaScreen(initData, key: state.pageKey), + ), + ), + GoRoute( + path: '/debug', + builder: (context, state) => DefaultAssetBundle( + bundle: FirkaBundle(), + child: DebugScreen(initData, key: state.pageKey), + ), + ), + GoRoute( + path: '/settings', + builder: (context, state) { + final items = state.extra != null + ? state.extra! as LinkedHashMap + : initData.settings.items; + return DefaultAssetBundle( + bundle: FirkaBundle(), + child: SettingsScreen(initData, items, key: state.pageKey), + ); + }, + ), + GoRoute( + path: '/message', + builder: (context, state) { + final info = state.extra as InfoBoardItem?; + if (info == null) { + return const SizedBox.shrink(); + } + return DefaultAssetBundle( + bundle: FirkaBundle(), + child: MessageScreen(initData, info, key: state.pageKey), + ); + }, + ), + StatefulShellRoute( + builder: (context, state, navigationShell) => DefaultAssetBundle( + bundle: FirkaBundle(), + child: HomeScreen( + child: ShellWithNavBar( + navigationShell: navigationShell, + child: navigationShell, + ), + ), + ), + navigatorContainerBuilder: (context, navigationShell, children) { + return SwipableNavigatorContainer( + navigationShell: navigationShell, + children: children, + ); + }, + branches: [ + StatefulShellBranch( + routes: [ + GoRoute( + path: '/home', + pageBuilder: (context, state) => NoTransitionPage( + key: state.pageKey, + child: DefaultAssetBundle( + bundle: FirkaBundle(), + child: HomeMainScreen( + initData, + homeUpdateNotifier, + homeUpdateFinishedNotifier, + ), + ), + ), + routes: [ + GoRoute( + path: 'subject/:uid', + builder: (context, state) { + final uid = state.pathParameters['uid'] ?? ''; + activeSubjectUid = uid; + return DefaultAssetBundle( + bundle: FirkaBundle(), + child: HomeGradesSubjectScreen( + initData, + homeUpdateNotifier, + homeUpdateFinishedNotifier, + ), + ); + }, + ), + ], + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: '/grades', + pageBuilder: (context, state) => NoTransitionPage( + key: state.pageKey, + child: DefaultAssetBundle( + bundle: FirkaBundle(), + child: HomeGradesScreen( + initData, + homeUpdateNotifier, + homeUpdateFinishedNotifier, + ), + ), + ), + routes: [ + GoRoute( + path: 'subject/:uid', + builder: (context, state) { + final uid = state.pathParameters['uid'] ?? ''; + activeSubjectUid = uid; + return DefaultAssetBundle( + bundle: FirkaBundle(), + child: HomeGradesSubjectScreen( + initData, + homeUpdateNotifier, + homeUpdateFinishedNotifier, + ), + ); + }, + ), + ], + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: '/timetable', + pageBuilder: (context, state) => NoTransitionPage( + key: state.pageKey, + child: DefaultAssetBundle( + bundle: FirkaBundle(), + child: HomeTimetableScreen( + initData, + homeUpdateNotifier, + homeUpdateFinishedNotifier, + ), + ), + ), + routes: [ + GoRoute( + path: 'monthly', + builder: (context, state) => DefaultAssetBundle( + bundle: FirkaBundle(), + child: HomeTimetableMonthlyScreen( + initData, + homeUpdateNotifier, + homeUpdateFinishedNotifier, + ), + ), + ), + GoRoute( + path: 'subject/:uid', + builder: (context, state) { + final uid = state.pathParameters['uid'] ?? ''; + activeSubjectUid = uid; + return DefaultAssetBundle( + bundle: FirkaBundle(), + child: HomeGradesSubjectScreen( + initData, + homeUpdateNotifier, + homeUpdateFinishedNotifier, + ), + ); + }, + ), + ], + ), + ], + ), + ], + ), + ], + ); +} + +String get _initialLocation { + if (!initDone) return '/'; + if (initData.tokens.isEmpty) return '/login'; + final betaWarning = initData.settings + .group('settings') + .boolean('beta_warning'); + if (!betaWarning) return '/beta'; + return '/home'; +} + +String? _redirect(BuildContext context, GoRouterState state) { + if (!initDone) return null; + final location = state.matchedLocation; + final hasTokens = initData.tokens.isNotEmpty; + final betaWarning = initData.settings + .group('settings') + .boolean('beta_warning'); + + if (!hasTokens) { + if (location != '/login') return '/login'; + return null; + } + + if (!betaWarning && location != '/beta') { + return '/beta'; + } + + if (betaWarning && location == '/beta') { + return '/home'; + } + + if (hasTokens && location == '/login') { + return '/home'; + } + + if (location == '/' || location.isEmpty) { + return '/home'; + } + + return null; +} diff --git a/firka/lib/routing/shell_with_nav_bar.dart b/firka/lib/routing/shell_with_nav_bar.dart new file mode 100644 index 0000000..02be222 --- /dev/null +++ b/firka/lib/routing/shell_with_nav_bar.dart @@ -0,0 +1,118 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:go_router/go_router.dart'; +import 'package:majesticons_flutter/majesticons_flutter.dart'; + +import 'package:firka/app/app_state.dart'; +import 'package:firka/ui/phone/pages/extras/extras.dart'; +import 'package:firka/ui/theme/style.dart'; +import 'package:firka/ui/phone/widgets/bottom_nav_icon.dart'; + +class ShellWithNavBar extends StatelessWidget { + const ShellWithNavBar({ + super.key, + required this.navigationShell, + required this.child, + }); + + final StatefulNavigationShell navigationShell; + final Widget child; + + @override + Widget build(BuildContext context) { + final data = initData; + final currentIndex = navigationShell.currentIndex; + + return Scaffold( + backgroundColor: appStyle.colors.background, + body: child, + bottomNavigationBar: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + appStyle.colors.background, + appStyle.colors.background.withValues(alpha: 0.0), + ], + stops: const [0.0, 1.0], + ), + ), + width: MediaQuery.sizeOf(context).width, + child: SafeArea( + top: false, + child: Padding( + padding: const EdgeInsets.fromLTRB(55, 0, 55, 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + BottomNavIconWidget( + () { + if (currentIndex != 0) { + navigationShell.goBranch(0); + } + }, + currentIndex == 0, + currentIndex == 0 + ? Majesticon.homeSolid + : Majesticon.homeLine, + data.l10n.home, + currentIndex == 0 + ? appStyle.colors.accent + : appStyle.colors.secondary, + appStyle.colors.textPrimary, + ), + BottomNavIconWidget( + () { + if (currentIndex != 1) { + navigationShell.goBranch(1); + } + }, + currentIndex == 1, + currentIndex == 1 + ? Majesticon.bookmarkSolid + : Majesticon.bookmarkLine, + data.l10n.grades, + currentIndex == 1 + ? appStyle.colors.accent + : appStyle.colors.secondary, + appStyle.colors.textPrimary, + ), + BottomNavIconWidget( + () { + if (currentIndex != 2) { + navigationShell.goBranch(2); + } + }, + currentIndex == 2, + currentIndex == 2 + ? Majesticon.calendarSolid + : Majesticon.calendarLine, + data.l10n.timetable, + currentIndex == 2 + ? appStyle.colors.accent + : appStyle.colors.secondary, + appStyle.colors.textPrimary, + ), + BottomNavIconWidget( + () { + HapticFeedback.lightImpact(); + showExtrasBottomSheet(context, data); + }, + false, + data.profilePicture != null + ? data.profilePicture! + : Majesticon.menuLine, + data.l10n.other, + appStyle.colors.secondary, + appStyle.colors.textPrimary, + isProfilePicture: data.profilePicture != null, + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/firka/lib/routing/swipable_navigator_container.dart b/firka/lib/routing/swipable_navigator_container.dart new file mode 100644 index 0000000..c973043 --- /dev/null +++ b/firka/lib/routing/swipable_navigator_container.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:go_router/go_router.dart'; + +class SwipableNavigatorContainer extends StatefulWidget { + const SwipableNavigatorContainer({ + super.key, + required this.navigationShell, + required this.children, + }); + + final StatefulNavigationShell navigationShell; + final List children; + + @override + State createState() => + _SwipableNavigatorContainerState(); +} + +class _SwipableNavigatorContainerState + extends State { + late PageController _pageController; + bool _isAnimating = false; + bool _isFirstBuild = true; + + @override + void initState() { + super.initState(); + _pageController = PageController( + initialPage: widget.navigationShell.currentIndex, + ); + } + + @override + void didUpdateWidget(SwipableNavigatorContainer oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.navigationShell.currentIndex != + widget.navigationShell.currentIndex) { + _syncToShellIndex(); + } + } + + void _syncToShellIndex() { + if (_isFirstBuild) { + _isFirstBuild = false; + if (_pageController.hasClients && + _pageController.page?.round() != + widget.navigationShell.currentIndex) { + _pageController.jumpToPage(widget.navigationShell.currentIndex); + } + return; + } + if (!_pageController.hasClients) return; + if (_pageController.page?.round() == widget.navigationShell.currentIndex) { + return; + } + _isAnimating = true; + _pageController + .animateToPage( + widget.navigationShell.currentIndex, + duration: const Duration(milliseconds: 175), + curve: Curves.easeInOut, + ) + .then((_) { + if (mounted) _isAnimating = false; + }); + } + + void _onPageChanged(int index) { + if (_isAnimating) return; + if (index != widget.navigationShell.currentIndex) { + HapticFeedback.heavyImpact(); + widget.navigationShell.goBranch(index); + } + } + + @override + void dispose() { + _pageController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return PageView( + controller: _pageController, + physics: const ClampingScrollPhysics(), + onPageChanged: _onPageChanged, + children: widget.children, + ); + } +} diff --git a/firka/lib/ui/components/common_bottom_sheets.dart b/firka/lib/ui/components/common_bottom_sheets.dart index adfd98a..24a6ce6 100644 --- a/firka/lib/ui/components/common_bottom_sheets.dart +++ b/firka/lib/ui/components/common_bottom_sheets.dart @@ -16,8 +16,9 @@ import 'package:intl/intl.dart'; import 'package:firka/app/app_state.dart'; import 'package:firka/ui/theme/style.dart'; -import 'package:firka/ui/phone/screens/home/home_screen.dart'; +import 'package:firka/ui/phone/pages/home/home_grades.dart'; import 'package:firka/ui/phone/widgets/lesson.dart'; +import 'package:go_router/go_router.dart'; import 'package:firka/ui/shared/class_icon.dart'; import 'package:firka/api/model/timetable.dart'; import 'package:firka/ui/components/firka_card.dart'; @@ -295,11 +296,14 @@ Future showLessonBottomSheet( color: appStyle.colors.buttonSecondaryFill, ), onTap: () { + activeSubjectUid = lesson.subject!.uid; + subjectName = lesson.subject!.name; + subjectId = lesson.subject!.uid; + subjectCategory = lesson.subject!.category.name ?? ""; + subjectInfo = []; Navigator.pop(context); - pageNavNotifier.value = PageNavData( - HomePage.grades, - lesson.subject!.uid, - lesson.subject!.name, + context.push( + '/timetable/subject/${lesson.subject!.uid}', ); }, ), @@ -728,11 +732,15 @@ Future showGradeBottomSheet( color: appStyle.colors.buttonSecondaryFill, ), onTap: () { + activeSubjectUid = grade.subject.uid; + subjectName = grade.subject.name; + subjectId = grade.subject.uid; + subjectCategory = + grade.subject.category.name ?? ""; + subjectInfo = []; Navigator.pop(context); - pageNavNotifier.value = PageNavData( - HomePage.grades, - grade.subject.uid, - grade.subject.name, + context.go( + '/grades/subject/${grade.subject.uid}', ); }, ), @@ -955,12 +963,13 @@ Future showHomeworkBottomSheet( color: appStyle.colors.buttonSecondaryFill, ), onTap: () { + activeSubjectUid = homework.subject.uid; + subjectName = homework.subjectName; + subjectId = homework.subject.uid; + subjectCategory = ""; + subjectInfo = []; Navigator.pop(context); - pageNavNotifier.value = PageNavData( - HomePage.grades, - homework.subject.uid, - homework.subjectName, - ); + context.push('/home/subject/${homework.subject.uid}'); }, ), ), diff --git a/firka/lib/ui/phone/pages/extras/extras.dart b/firka/lib/ui/phone/pages/extras/extras.dart index 54fa3bb..0dba121 100644 --- a/firka/lib/ui/phone/pages/extras/extras.dart +++ b/firka/lib/ui/phone/pages/extras/extras.dart @@ -3,14 +3,11 @@ import 'package:firka/core/settings.dart'; import 'package:firka/ui/components/firka_shadow.dart'; import 'package:firka/app/app_state.dart'; import 'package:firka/ui/theme/style.dart'; -import 'package:firka/ui/phone/screens/settings/settings_screen.dart'; import 'package:firka/ui/shared/firka_icon.dart'; import 'package:flutter/material.dart'; import 'package:majesticons_flutter/majesticons_flutter.dart'; -import 'package:firka/core/firka_bundle.dart'; -import 'package:firka/ui/phone/screens/debug/debug_screen.dart'; -import 'package:firka/ui/phone/screens/home/home_screen.dart'; +import 'package:go_router/go_router.dart'; void showExtrasBottomSheet(BuildContext context, AppInitialization data) { Widget Function(double) debugBtn = (_) => const SizedBox(); @@ -20,17 +17,9 @@ void showExtrasBottomSheet(BuildContext context, AppInitialization data) { if (isDeveloper()) { debugBtn = (double itemWidth) => GestureDetector( // Fejlesztői menü - onTap: () => { - Navigator.pop(context), - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => DefaultAssetBundle( - bundle: FirkaBundle(), - child: DebugScreen(data), - ), - ), - ), + onTap: () { + context.pop(); + context.push('/debug'); }, child: SizedBox( height: 60, @@ -126,20 +115,11 @@ void showExtrasBottomSheet(BuildContext context, AppInitialization data) { GestureDetector( // Fiókod onTap: () { - Navigator.pop(context); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - DefaultAssetBundle( - bundle: FirkaBundle(), - child: SettingsScreen( - data, - data.settings.items.group( - "profile_settings", - ), - ), - ), + context.pop(); + context.push( + '/settings', + extra: data.settings.items.group( + "profile_settings", ), ); }, @@ -189,20 +169,8 @@ void showExtrasBottomSheet(BuildContext context, AppInitialization data) { GestureDetector( // Beállítás onTap: () { - Navigator.pop(context); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - DefaultAssetBundle( - bundle: FirkaBundle(), - child: SettingsScreen( - data, - data.settings.items, - ), - ), - ), - ); + context.pop(); + context.push('/settings'); }, child: SizedBox( height: 60, @@ -289,22 +257,8 @@ void showExtrasBottomSheet(BuildContext context, AppInitialization data) { .group("settings")["developer_enabled"]! .postUpdate(); - Navigator.of( - navigatorKey.currentContext!, - ).popUntil((route) => false); - Navigator.push( - navigatorKey.currentContext!, - MaterialPageRoute( - builder: (context) => DefaultAssetBundle( - bundle: FirkaBundle(), - child: HomeScreen( - data, - false, - key: ValueKey('homeScreen'), - ), - ), - ), - ); + context.pop(); + context.go('/home'); } else if (debugCounter < 10) { debugCounter++; } diff --git a/firka/lib/ui/phone/pages/home/home_grades.dart b/firka/lib/ui/phone/pages/home/home_grades.dart index 084d164..903ad3b 100644 --- a/firka/lib/ui/phone/pages/home/home_grades.dart +++ b/firka/lib/ui/phone/pages/home/home_grades.dart @@ -6,6 +6,7 @@ import 'package:firka/ui/components/grade_helpers.dart'; import 'package:firka/ui/phone/widgets/grade_chart.dart'; import 'package:firka/ui/shared/grade_small_card.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:firka/api/consts.dart'; import 'package:firka/api/model/class_group.dart'; @@ -23,13 +24,11 @@ class HomeGradesScreen extends StatefulWidget { final AppInitialization data; final UpdateNotifier updateNotifier; final UpdateNotifier finishNotifier; - final void Function(int) pageController; const HomeGradesScreen( this.data, this.updateNotifier, - this.finishNotifier, - this.pageController, { + this.finishNotifier, { super.key, }); @@ -185,7 +184,7 @@ class _HomeGradesScreen extends FirkaState { subjectInfo = subjects .where((s) => s.uid == subject.uid) .toList(); - widget.pageController(1); + context.go('/grades/subject/${subject.uid}'); }, ), ); @@ -201,7 +200,7 @@ class _HomeGradesScreen extends FirkaState { subjectInfo = subjects .where((s) => s.uid == subject.uid) .toList(); - widget.pageController(1); + context.go('/grades/subject/${subject.uid}'); }, ), ); diff --git a/firka/lib/ui/phone/pages/home/home_grades_subject.dart b/firka/lib/ui/phone/pages/home/home_grades_subject.dart index 48b5cae..94ea8fc 100644 --- a/firka/lib/ui/phone/pages/home/home_grades_subject.dart +++ b/firka/lib/ui/phone/pages/home/home_grades_subject.dart @@ -7,6 +7,7 @@ import 'package:firka/ui/phone/pages/home/home_grades.dart'; import 'package:firka/ui/shared/class_icon.dart'; import 'package:firka/ui/shared/firka_icon.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:flutter_svg/svg.dart'; import 'package:majesticons_flutter/majesticons_flutter.dart'; @@ -19,13 +20,11 @@ class HomeGradesSubjectScreen extends StatefulWidget { final AppInitialization data; final UpdateNotifier updateNotifier; final UpdateNotifier finishNotifier; - final void Function(int) pageController; const HomeGradesSubjectScreen( this.data, this.updateNotifier, - this.finishNotifier, - this.pageController, { + this.finishNotifier, { super.key, }); @@ -142,235 +141,241 @@ class _HomeGradesSubjectScreen extends FirkaState { } } - return Padding( - padding: const EdgeInsets.only(left: 16.0, right: 16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 12), - Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Transform.translate( - offset: const Offset(-4, 0), - child: GestureDetector( - child: FirkaIconWidget( - FirkaIconType.majesticons, - Majesticon.chevronLeftLine, - color: appStyle.colors.textSecondary, - ), - onTap: () { - widget.pageController(0); - }, - ), - ), - Transform.translate( - offset: const Offset(-4, 0), - child: Text( - widget.data.l10n.subjects, - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textPrimary, - ), - ), - ), - ], - ), - GestureDetector( - child: Card( - color: appStyle.colors.buttonSecondaryFill, - child: Padding( - padding: const EdgeInsets.all(4), - child: FirkaIconWidget( - FirkaIconType.majesticons, - Majesticon.menuSolid, - size: 26.0, - color: appStyle.colors.accent, - ), - ), - ), - onTap: () { - // Navigator.push(context, Settings) - // showSubjectBottomSheetSettings( - // context, - // widget.data, - // aGrade.subject, - // ); - }, - ), - ], - ), - ], - ), - // SizedBox(height: 16), - // GradeChart(grades: grades?.toList() ?? []), - SizedBox( - height: - MediaQuery.of(context).size.height - - MediaQuery.of(context).padding.top - - 230, - child: ListView( + return Material( + color: appStyle.colors.background, + child: Padding( + padding: const EdgeInsets.only(left: 16.0, right: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 12), + Column( children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Card( - shadowColor: const Color.fromRGBO(0, 0, 0, 0), - color: appStyle.colors.a15p, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - child: Padding( - padding: EdgeInsetsGeometry.all(6), - child: ClassIconWidget( - uid: aGrade.subject.uid, - className: aGrade.subject.name, - category: aGrade.subject.category.name!, - color: appStyle.colors.accent, - ), - ), - ), - SizedBox(height: 8), - Text( - aGrade.subject.name, - style: appStyle.fonts.H_H2.apply( - color: appStyle.colors.textPrimary, - ), - ), - SizedBox(height: 2), - Text( - aGrade.teacher, - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textSecondary, - ), - ), - SizedBox(height: 15), - ], - ), - Padding( - padding: EdgeInsets.only(left: 4), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: gradeWidgets, - ), - ), - ], - ), - ), - ], - ), - ); - } else { - return Padding( - padding: const EdgeInsets.only(left: 16.0, right: 16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - children: [ - Row( - children: [ - Transform.translate( - offset: const Offset(-4, 0), - child: GestureDetector( - child: FirkaIconWidget( - FirkaIconType.majesticons, - Majesticon.chevronLeftLine, - color: appStyle.colors.textSecondary, - ), - onTap: () { - widget.pageController(0); - }, - ), - ), - Transform.translate( - offset: const Offset(-4, 1), - child: Text( - widget.data.l10n.subjects, - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textPrimary, - ), - ), - ), - ], - ), - ], - ), - SizedBox(height: 16), - SizedBox( - height: - MediaQuery.of(context).size.height - - MediaQuery.of(context).padding.top - - 230, - child: ListView( - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Card( - shadowColor: const Color.fromRGBO(0, 0, 0, 0), - color: appStyle.colors.a15p, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - child: Padding( - padding: EdgeInsetsGeometry.all(6), - child: ClassIconWidget( - uid: subjectId, - className: subjectName, - category: subjectCategory, - color: appStyle.colors.accent, - ), - ), - ), - SizedBox(height: 8), - Text( - subjectName, - style: appStyle.fonts.H_H2.apply( - color: appStyle.colors.textPrimary, - ), - ), - SizedBox(height: 2), - Text( - widget.data.l10n.unknown_teacher, - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textSecondary, - ), - ), - ], - ), - SizedBox( - height: - MediaQuery.of(context).size.height - - MediaQuery.of(context).padding.top - - 320, - child: Center( - child: Column( - mainAxisSize: MainAxisSize.min, + Row( children: [ - SvgPicture.asset( - "assets/images/logos/dave.svg", - width: 48, - height: 48, + Transform.translate( + offset: const Offset(-4, 0), + child: GestureDetector( + child: FirkaIconWidget( + FirkaIconType.majesticons, + Majesticon.chevronLeftLine, + color: appStyle.colors.textSecondary, + ), + onTap: () { + context.pop(); + }, + ), ), - SizedBox(height: 12), - Text( - widget.data.l10n.no_grades, - style: appStyle.fonts.B_16R.apply( - color: appStyle.colors.textSecondary, + Transform.translate( + offset: const Offset(-4, 0), + child: Text( + widget.data.l10n.subjects, + style: appStyle.fonts.B_16R.apply( + color: appStyle.colors.textPrimary, + ), ), ), ], ), - ), + GestureDetector( + child: Card( + color: appStyle.colors.buttonSecondaryFill, + child: Padding( + padding: const EdgeInsets.all(4), + child: FirkaIconWidget( + FirkaIconType.majesticons, + Majesticon.menuSolid, + size: 26.0, + color: appStyle.colors.accent, + ), + ), + ), + onTap: () { + // Navigator.push(context, Settings) + // showSubjectBottomSheetSettings( + // context, + // widget.data, + // aGrade.subject, + // ); + }, + ), + ], ), ], ), - ), - ], + // SizedBox(height: 16), + // GradeChart(grades: grades?.toList() ?? []), + SizedBox( + height: + MediaQuery.of(context).size.height - + MediaQuery.of(context).padding.top - + 230, + child: ListView( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Card( + shadowColor: const Color.fromRGBO(0, 0, 0, 0), + color: appStyle.colors.a15p, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: EdgeInsetsGeometry.all(6), + child: ClassIconWidget( + uid: aGrade.subject.uid, + className: aGrade.subject.name, + category: aGrade.subject.category.name!, + color: appStyle.colors.accent, + ), + ), + ), + SizedBox(height: 8), + Text( + aGrade.subject.name, + style: appStyle.fonts.H_H2.apply( + color: appStyle.colors.textPrimary, + ), + ), + SizedBox(height: 2), + Text( + aGrade.teacher, + style: appStyle.fonts.B_16R.apply( + color: appStyle.colors.textSecondary, + ), + ), + SizedBox(height: 15), + ], + ), + Padding( + padding: EdgeInsets.only(left: 4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: gradeWidgets, + ), + ), + ], + ), + ), + ], + ), + ), + ); + } else { + return Material( + color: appStyle.colors.background, + child: Padding( + padding: const EdgeInsets.only(left: 16.0, right: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + children: [ + Row( + children: [ + Transform.translate( + offset: const Offset(-4, 0), + child: GestureDetector( + child: FirkaIconWidget( + FirkaIconType.majesticons, + Majesticon.chevronLeftLine, + color: appStyle.colors.textSecondary, + ), + onTap: () { + context.pop(); + }, + ), + ), + Transform.translate( + offset: const Offset(-4, 1), + child: Text( + widget.data.l10n.subjects, + style: appStyle.fonts.B_16R.apply( + color: appStyle.colors.textPrimary, + ), + ), + ), + ], + ), + ], + ), + SizedBox(height: 16), + SizedBox( + height: + MediaQuery.of(context).size.height - + MediaQuery.of(context).padding.top - + 230, + child: ListView( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Card( + shadowColor: const Color.fromRGBO(0, 0, 0, 0), + color: appStyle.colors.a15p, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: EdgeInsetsGeometry.all(6), + child: ClassIconWidget( + uid: subjectId, + className: subjectName, + category: subjectCategory, + color: appStyle.colors.accent, + ), + ), + ), + SizedBox(height: 8), + Text( + subjectName, + style: appStyle.fonts.H_H2.apply( + color: appStyle.colors.textPrimary, + ), + ), + SizedBox(height: 2), + Text( + widget.data.l10n.unknown_teacher, + style: appStyle.fonts.B_16R.apply( + color: appStyle.colors.textSecondary, + ), + ), + ], + ), + SizedBox( + height: + MediaQuery.of(context).size.height - + MediaQuery.of(context).padding.top - + 320, + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset( + "assets/images/logos/dave.svg", + width: 48, + height: 48, + ), + SizedBox(height: 12), + Text( + widget.data.l10n.no_grades, + style: appStyle.fonts.B_16R.apply( + color: appStyle.colors.textSecondary, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), ), ); } diff --git a/firka/lib/ui/phone/pages/home/home_main.dart b/firka/lib/ui/phone/pages/home/home_main.dart index 2670d12..72f81d7 100644 --- a/firka/lib/ui/phone/pages/home/home_main.dart +++ b/firka/lib/ui/phone/pages/home/home_main.dart @@ -4,13 +4,13 @@ import 'package:firka/api/client/kreta_stream.dart'; import 'package:firka/api/model/grade.dart'; import 'package:firka/core/extensions.dart'; import 'package:firka/ui/components/common_bottom_sheets.dart'; -import 'package:firka/ui/phone/screens/message/message_screen.dart'; import 'package:firka/ui/phone/widgets/home_main_starting_soon.dart'; import 'package:firka/ui/phone/widgets/homework.dart'; import 'package:firka/ui/phone/widgets/info_board_item.dart'; import 'package:firka/ui/phone/widgets/lesson_small.dart'; import 'package:firka/ui/shared/delayed_spinner.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:majesticons_flutter/majesticons_flutter.dart'; import 'package:firka/api/model/homework.dart'; @@ -326,11 +326,7 @@ class _HomeMainScreen extends FirkaState { GestureDetector( child: InfoBoardItemWidget(item), onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => MessageScreen(widget.data, item), - ), - ); + context.push('/message', extra: item); }, ), item.date, diff --git a/firka/lib/ui/phone/pages/home/home_subpage.dart b/firka/lib/ui/phone/pages/home/home_subpage.dart deleted file mode 100644 index bc3ea64..0000000 --- a/firka/lib/ui/phone/pages/home/home_subpage.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:firka/ui/phone/screens/home/home_screen.dart'; -import 'package:flutter/material.dart'; - -import 'package:firka/core/state/firka_state.dart'; -import 'package:firka/core/state/update_notifier.dart'; -import 'package:firka/ui/theme/style.dart'; - -class PageWithSubPages extends StatefulWidget { - final int pageIndex; - final List subPages; - final ValueNotifier subPageActive; - final UpdateNotifier back; - - const PageWithSubPages( - this.subPages, - this.subPageActive, - this.back, { - super.key, - required this.pageIndex, - }); - - @override - PageWithSubPagesState createState() => PageWithSubPagesState(); -} - -class PageWithSubPagesState extends FirkaState { - int _currentSubPage = 0; - - @override - void initState() { - super.initState(); - - widget.back.addListener(_backListener); - } - - void _backListener() { - if (!mounted) return; - - setState(() { - subPageActive.value = false; - _currentSubPage = 0; - }); - } - - @override - void didUpdateWidget(PageWithSubPages oldWidget) { - super.didUpdateWidget(oldWidget); - - widget.back.removeListener(_backListener); - widget.back.addListener(_backListener); - } - - @override - void dispose() { - super.dispose(); - - widget.back.removeListener(_backListener); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: appStyle.colors.background, - body: widget.subPages[_currentSubPage]((page) { - subPageActive.value = _currentSubPage == 0; - setState(() { - _currentSubPage = page; - }); - }), - ); - } -} diff --git a/firka/lib/ui/phone/pages/home/home_timetable.dart b/firka/lib/ui/phone/pages/home/home_timetable.dart index 107be65..6bb550f 100644 --- a/firka/lib/ui/phone/pages/home/home_timetable.dart +++ b/firka/lib/ui/phone/pages/home/home_timetable.dart @@ -13,6 +13,7 @@ import 'package:firka/ui/phone/screens/settings/settings_screen.dart'; import 'package:firka/ui/phone/widgets/bubble_test.dart'; import 'package:firka/ui/shared/delayed_spinner.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:flutter/services.dart'; import 'package:majesticons_flutter/majesticons_flutter.dart'; import 'package:transparent_pointer/transparent_pointer.dart'; @@ -29,13 +30,11 @@ class HomeTimetableScreen extends StatefulWidget { final AppInitialization data; final UpdateNotifier updateNotifier; final UpdateNotifier finishNotifier; - final void Function(int) pageController; const HomeTimetableScreen( this.data, this.updateNotifier, - this.finishNotifier, - this.pageController, { + this.finishNotifier, { super.key, }); @@ -555,7 +554,7 @@ class _HomeTimetableScreen extends FirkaState ), ), onTap: () { - widget.pageController(1); + context.push('/timetable/monthly'); }, ), /* TODO: 1.1.0 diff --git a/firka/lib/ui/phone/pages/home/home_timetable_mo.dart b/firka/lib/ui/phone/pages/home/home_timetable_mo.dart index a8fa296..11bf82c 100644 --- a/firka/lib/ui/phone/pages/home/home_timetable_mo.dart +++ b/firka/lib/ui/phone/pages/home/home_timetable_mo.dart @@ -7,6 +7,7 @@ import 'package:firka/core/settings.dart'; import 'package:firka/ui/theme/style.dart'; import 'package:firka/ui/shared/delayed_spinner.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:majesticons_flutter/majesticons_flutter.dart'; import 'package:transparent_pointer/transparent_pointer.dart'; @@ -22,13 +23,11 @@ class HomeTimetableMonthlyScreen extends StatefulWidget { final AppInitialization data; final UpdateNotifier updateNotifier; final UpdateNotifier finishNotifier; - final void Function(int) pageController; const HomeTimetableMonthlyScreen( this.data, this.updateNotifier, - this.finishNotifier, - this.pageController, { + this.finishNotifier, { super.key, }); @@ -392,7 +391,7 @@ class _HomeTimetableMonthlyScreen ), ), onTap: () { - widget.pageController(0); + context.pop(); }, ), // Nincs elkészítve jelenleg: Dolgozat stb hozzáadása(?) diff --git a/firka/lib/ui/phone/screens/debug/debug_screen.dart b/firka/lib/ui/phone/screens/debug/debug_screen.dart index 0d95dec..59e8c1e 100644 --- a/firka/lib/ui/phone/screens/debug/debug_screen.dart +++ b/firka/lib/ui/phone/screens/debug/debug_screen.dart @@ -7,12 +7,11 @@ import 'package:firka/core/extensions.dart'; import 'package:firka/core/icon_helper.dart'; import 'package:firka/core/profile_picture.dart'; import 'package:firka/app/app_state.dart'; -import 'package:firka/ui/phone/screens/login/login_screen.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import 'package:firka/core/debug_helper.dart'; -import 'package:firka/core/firka_bundle.dart'; import 'package:firka/core/state/firka_state.dart'; import 'package:firka/ui/shared/firka_icon.dart'; @@ -221,15 +220,7 @@ class _DebugScreen extends FirkaState { widget.data.tokens = List.empty(growable: true); if (!context.mounted) return; - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => DefaultAssetBundle( - bundle: FirkaBundle(), - child: LoginScreen(widget.data), - ), - ), - ); + context.go('/login'); }, child: const Text('wipe users'), ), diff --git a/firka/lib/ui/phone/screens/home/beta_screen.dart b/firka/lib/ui/phone/screens/home/beta_screen.dart index 8501bf2..a6c20ed 100644 --- a/firka/lib/ui/phone/screens/home/beta_screen.dart +++ b/firka/lib/ui/phone/screens/home/beta_screen.dart @@ -5,8 +5,8 @@ import 'package:firka/data/models/app_settings_model.dart'; import 'package:firka/core/settings.dart'; import 'package:firka/ui/components/firka_button.dart'; import 'package:firka/ui/theme/style.dart'; -import 'package:firka/ui/phone/screens/home/home_screen.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:firka/core/state/firka_state.dart'; import 'package:firka/app/app_state.dart'; @@ -123,12 +123,7 @@ class _BetaScreenState extends FirkaState { .group("settings")["beta_warning"]! .postUpdate(); if (!context.mounted) return; - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (context) => HomeScreen(widget.data, false), - ), - (route) => false, - ); + context.go('/home'); }, ), ], diff --git a/firka/lib/ui/phone/screens/home/home_screen.dart b/firka/lib/ui/phone/screens/home/home_screen.dart index 28ccf90..b7e40d5 100644 --- a/firka/lib/ui/phone/screens/home/home_screen.dart +++ b/firka/lib/ui/phone/screens/home/home_screen.dart @@ -3,131 +3,68 @@ import 'dart:io'; import 'package:firka/api/client/kreta_client.dart'; import 'package:firka/api/client/kreta_stream.dart'; import 'package:firka/api/exceptions/token.dart'; -import 'package:firka/services/active_account_helper.dart'; import 'package:firka/core/extensions.dart'; +import 'package:firka/core/firka_bundle.dart'; +import 'package:firka/services/active_account_helper.dart'; import 'package:firka/services/live_activity_service.dart'; import 'package:firka/core/settings.dart'; -import 'package:firka/core/state/update_notifier.dart'; import 'package:firka/services/watch_sync_helper.dart'; import 'package:firka/app/app_state.dart'; import 'package:firka/ui/theme/style.dart'; -import 'package:firka/ui/phone/pages/extras/main_wear_pair.dart'; import 'package:firka/ui/phone/pages/extras/reauth_toast.dart'; -import 'package:firka/ui/phone/pages/home/home_grades.dart'; -import 'package:firka/ui/phone/pages/home/home_main.dart'; -import 'package:firka/ui/phone/pages/home/home_subpage.dart'; -import 'package:firka/ui/phone/pages/home/home_timetable_mo.dart'; -import 'package:firka/ui/phone/screens/home/beta_screen.dart'; -import 'package:firka/ui/phone/widgets/bottom_nav_icon.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:home_widget/home_widget.dart'; import 'package:majesticons_flutter/majesticons_flutter.dart'; -import 'package:smart_scroll/smart_scroll.dart'; import 'package:firka/data/widget.dart'; import 'package:firka/core/debug_helper.dart'; -import 'package:firka/core/firka_bundle.dart'; import 'package:firka/core/state/firka_state.dart'; import 'package:firka/core/image_preloader.dart'; import 'package:firka/ui/shared/delayed_spinner.dart'; import 'package:firka/ui/shared/firka_icon.dart'; -import '../../pages/extras/extras.dart'; import '../../pages/extras/main_error.dart'; -import '../../pages/home/home_grades_subject.dart'; -import '../../pages/home/home_timetable.dart'; - -class PageNavData { - HomePage page; - String? subPageParams; - String subjectName; - String? subjectId; - String? subjectCategory; - PageNavData( - this.page, - this.subPageParams, - this.subjectName, { - this.subjectId, - this.subjectCategory, - }); -} - -final ValueNotifier pageNavNotifier = ValueNotifier( - PageNavData(HomePage.home, null, ""), -); -bool forcedNavPage = false; // TODO: this is a hack! - -class HomeScreen extends StatefulWidget { - final AppInitialization data; - final bool watchPair; - final String? model; - final UpdateNotifier updateNotifier = UpdateNotifier(); - final UpdateNotifier updateFinishedNotifier = UpdateNotifier(); - - HomeScreen(this.data, this.watchPair, {this.model, super.key}); - - @override - State createState() => _HomeScreenState(); -} - -enum HomePage { home, grades, timetable } enum ActiveToastType { fetching, error, reauth, none } bool _fetching = true; bool _prefetched = false; -bool canPop = true; -ValueNotifier subPageActive = ValueNotifier(false); -UpdateNotifier subPageBack = UpdateNotifier(); -HomePage homeScreenPage = HomePage.home; -List previousPages = List.empty(growable: true); +class HomeScreen extends StatefulWidget { + final Widget child; + + const HomeScreen({super.key, required this.child}); + + @override + State createState() => _HomeScreenState(); +} class _HomeScreenState extends FirkaState with WidgetsBindingObserver { - _HomeScreenState(); - - final PageController _pageController = PageController(); - HomePage? _pendingNavigation; - Widget? toast; - bool pairingDone = false; bool _disposed = false; bool _preloadDone = false; - int forcedNav = 0; bool _didRunSecondaryICloudRecovery = false; - final RefreshController _refreshController = RefreshController( - initialRefresh: false, - ); + bool _prefetchInProgress = false; + bool _didRunLiveActivityLogin = false; + bool _hasCompletedFirstPrefetch = false; ActiveToastType activeToast = ActiveToastType.none; - void setPageCB(HomePage newPage, bool setPrev) { - if (_disposed) return; - setState(() { - if (setPrev) previousPages.add(homeScreenPage); - canPop = false; - homeScreenPage = newPage; - }); - } - void _setupNotificationListener() { final notificationChannel = MethodChannel('firka.app/notifications'); notificationChannel.setMethodCallHandler((call) async { if (call.method == 'onNotificationTapped') { logger.info('Notification tapped: ${call.arguments}'); - final args = call.arguments as Map?; if (args == null) return; - final action = args['action'] as String?; final route = args['route'] as String?; - if (action != null || route != null) { logger.info('Navigating to timetable from notification'); - _navigateToPage(HomePage.timetable); + appRouter?.go('/timetable'); } } }); @@ -140,75 +77,32 @@ class _HomeScreenState extends FirkaState WidgetsBinding.instance.addPostFrameCallback((_) { widgetChannel.invokeMethod('getPendingDeepLink').then((link) { - if (link != null) { - _handleWidgetDeepLink(link); - } + if (link != null) _handleWidgetDeepLink(link); }); }); widgetChannel.setMethodCallHandler((call) async { if (call.method == 'onWidgetDeepLink') { final link = call.arguments as String?; - if (link != null) { - _handleWidgetDeepLink(link); - } + if (link != null) _handleWidgetDeepLink(link); } }); } void _handleWidgetDeepLink(String link) { logger.info('Widget deep link received: $link'); - - HomePage targetPage; switch (link) { case 'home': - targetPage = HomePage.home; + appRouter?.go('/home'); break; case 'timetable': - targetPage = HomePage.timetable; + appRouter?.go('/timetable'); break; case 'grades': - targetPage = HomePage.grades; + appRouter?.go('/grades'); break; default: logger.warning('Unknown widget deep link: $link'); - return; - } - - _navigateToPage(targetPage); - } - - void _navigateToPage(HomePage targetPage) { - if (_disposed) return; - - homeScreenPage = targetPage; - - if (_pageController.hasClients) { - setState(() { - _pageController.jumpToPage(targetPage.index); - }); - } else { - _pendingNavigation = targetPage; - WidgetsBinding.instance.addPostFrameCallback((_) { - _applyPendingNavigation(); - }); - } - } - - void _applyPendingNavigation() { - if (_disposed || _pendingNavigation == null) return; - - if (_pageController.hasClients) { - final target = _pendingNavigation!; - _pendingNavigation = null; - setState(() { - homeScreenPage = target; - _pageController.jumpToPage(target.index); - }); - } else { - WidgetsBinding.instance.addPostFrameCallback((_) { - _applyPendingNavigation(); - }); } } @@ -217,9 +111,9 @@ class _HomeScreenState extends FirkaState _didRunSecondaryICloudRecovery = true; final activeToken = pickActiveToken( - tokens: widget.data.tokens, - settings: widget.data.settings, - preferredStudentIdNorm: widget.data.client.model.studentIdNorm, + tokens: initData.tokens, + settings: initData.settings, + preferredStudentIdNorm: initData.client.model.studentIdNorm, ); final now = DateTime.now(); @@ -229,9 +123,7 @@ class _HomeScreenState extends FirkaState activeToken.expiryDate == null || activeToken.expiryDate!.isBefore(now.add(const Duration(seconds: 60))); - if (!shouldRunRecovery) { - return; - } + if (!shouldRunRecovery) return; logger.info( '[Home] Secondary iCloud recovery scheduled (5s delay, startup safety pass)', @@ -241,25 +133,25 @@ class _HomeScreenState extends FirkaState try { final recovered = await WatchSyncHelper.checkAndRecoverFromiCloud( - isar: widget.data.isar, - tokens: widget.data.tokens, - client: widget.data.client, + isar: initData.isar, + tokens: initData.tokens, + client: initData.client, ); if (!recovered) { logger.info('[Home] Secondary iCloud recovery found no fresher token'); return; } - final refreshedTokens = initDone ? initData.tokens : widget.data.tokens; - widget.data.tokens = refreshedTokens; + final refreshedTokens = initDone ? initData.tokens : initData.tokens; + initData.tokens = refreshedTokens; final selectedToken = pickActiveToken( tokens: refreshedTokens, - settings: widget.data.settings, - preferredStudentIdNorm: widget.data.client.model.studentIdNorm, + settings: initData.settings, + preferredStudentIdNorm: initData.client.model.studentIdNorm, ); if (selectedToken != null) { - widget.data.client.model = selectedToken; + initData.client.model = selectedToken; } KretaClient.clearReauthFlag(); logger.info('[Home] Secondary iCloud recovery applied a fresher token'); @@ -268,9 +160,6 @@ class _HomeScreenState extends FirkaState } } - bool _prefetchInProgress = false; - bool _didRunLiveActivityLogin = false; - void prefetch() async { if (_prefetched) return; if (_prefetchInProgress) return; @@ -290,7 +179,7 @@ class _HomeScreenState extends FirkaState await _runSecondaryICloudRecoveryIfNeeded(); try { - await widget.data.client.refreshTokenProactively().timeout( + await initData.client.refreshTokenProactively().timeout( const Duration(seconds: 60), onTimeout: () { logger.warning('[Home] Token refresh/recovery timed out after 60s'); @@ -304,7 +193,7 @@ class _HomeScreenState extends FirkaState await fetchData(); if (Platform.isAndroid) { - await WidgetCacheHelper.updateWidgetCache(appStyle, widget.data.client); + await WidgetCacheHelper.updateWidgetCache(appStyle, initData.client); await HomeWidget.updateWidget( qualifiedAndroidName: "app.firka.naplo.glance.TimetableWidget", ); @@ -312,21 +201,21 @@ class _HomeScreenState extends FirkaState if (Platform.isIOS) { await WidgetCacheHelper.refreshIOSWidgets( - widget.data.client, - widget.data.settings, + initData.client, + initData.settings, ); if (!_didRunLiveActivityLogin) { _didRunLiveActivityLogin = true; final token = pickActiveToken( - tokens: widget.data.tokens, - settings: widget.data.settings, + tokens: initData.tokens, + settings: initData.settings, ); final studentName = token?.studentId ?? "Student"; LiveActivityService.onUserLogin( - client: widget.data.client, + client: initData.client, studentName: studentName, - settingsStore: widget.data.settings, + settingsStore: initData.settings, ).catchError((e, st) { logger.severe('LiveActivity registration failed: $e', e, st); }); @@ -337,7 +226,7 @@ class _HomeScreenState extends FirkaState (LiveActivityService.isTokenExpired || KretaClient.needsReauth)) { activeToast = ActiveToastType.reauth; setState(() { - toast = buildReauthToast(context, widget.data, () { + toast = buildReauthToast(context, initData, () { if (!_disposed) { setState(() { activeToast = ActiveToastType.none; @@ -351,10 +240,9 @@ class _HomeScreenState extends FirkaState } catch (e) { if (e is TokenExpiredException || e is InvalidGrantException) { activeToast = ActiveToastType.reauth; - if (_disposed) return; setState(() { - toast = buildReauthToast(context, widget.data, () { + toast = buildReauthToast(context, initData, () { if (!_disposed) { setState(() { activeToast = ActiveToastType.none; @@ -367,11 +255,8 @@ class _HomeScreenState extends FirkaState } activeToast = ActiveToastType.error; - var dismissDelay = 120; - if (kDebugMode) { - dismissDelay = 2; - } + if (kDebugMode) dismissDelay = 2; Timer(Duration(seconds: dismissDelay), () { if (_disposed) return; setState(() { @@ -382,7 +267,6 @@ class _HomeScreenState extends FirkaState if (_disposed) return; setState(() { - // TODO: Make this and the error toast more rounded toast = Positioned( top: MediaQuery.of(context).size.height / 1.6, left: 0.0, @@ -401,7 +285,7 @@ class _HomeScreenState extends FirkaState mainAxisSize: MainAxisSize.min, children: [ Text( - widget.data.l10n.api_error, + initData.l10n.api_error, style: appStyle.fonts.B_16SB.copyWith( color: appStyle.colors.errorText, ), @@ -435,7 +319,6 @@ class _HomeScreenState extends FirkaState if (!_disposed) { setState(() { _fetching = false; - if (activeToast == ActiveToastType.fetching) toast = null; }); } @@ -453,7 +336,7 @@ class _HomeScreenState extends FirkaState final midnight = timeNow().getMidnight(); - widget.data.client + initData.client .getTimeTableStream( midnight, midnight.add(Duration(hours: 23, minutes: 59)), @@ -463,27 +346,27 @@ class _HomeScreenState extends FirkaState lessonsFetched++; }); - widget.data.client.getNoticeBoardStream(cacheOnly: false).forEach((items) { + initData.client.getNoticeBoardStream(cacheOnly: false).forEach((items) { noticeBoardFetched++; }); - widget.data.client.getInfoBoardStream(cacheOnly: false).forEach((items) { + initData.client.getInfoBoardStream(cacheOnly: false).forEach((items) { infoBoardFetched++; }); - widget.data.client.getStudentStream(cacheOnly: false).forEach((student) { + initData.client.getStudentStream(cacheOnly: false).forEach((student) { studentFetched++; }); - widget.data.client.getTestsStream(cacheOnly: false).forEach((tests) { + initData.client.getTestsStream(cacheOnly: false).forEach((tests) { testsFetched++; }); - widget.data.client.getGradesStream(cacheOnly: false).forEach((grades) { + initData.client.getGradesStream(cacheOnly: false).forEach((grades) { gradesFetched++; }); - widget.data.client.getHomeworkStream(cacheOnly: false).forEach((homework) { + initData.client.getHomeworkStream(cacheOnly: false).forEach((homework) { homeworkFetched++; }); @@ -511,13 +394,8 @@ class _HomeScreenState extends FirkaState WidgetsBinding.instance.addObserver(this); - widget.data.settingsUpdateNotifier.addListener(settingsUpdateListener); - - widget.data.profilePictureUpdateNotifier.addListener( - _onProfilePictureUpdated, - ); - - // Listen for reauth state changes (e.g., when Watch sends a valid token) + initData.settingsUpdateNotifier.addListener(settingsUpdateListener); + initData.profilePictureUpdateNotifier.addListener(_onProfilePictureUpdated); KretaClient.reauthStateNotifier.addListener(_onReauthStateChanged); _setupNotificationListener(); @@ -527,7 +405,7 @@ class _HomeScreenState extends FirkaState _preloadImages(); if (Platform.isIOS && - widget.data.settings.group("settings").boolean("beta_warning")) { + initData.settings.group("settings").boolean("beta_warning")) { Future.delayed(Duration(seconds: 3), () async { await LiveActivityService.showConsentScreenIfNeeded(); }); @@ -536,9 +414,7 @@ class _HomeScreenState extends FirkaState void _onReauthStateChanged() { if (!mounted || _disposed) return; - // If reauth is no longer needed, dismiss the reauth toast if (!KretaClient.needsReauth && activeToast == ActiveToastType.reauth) { - debugPrint('[HomeScreen] Reauth flag cleared, dismissing toast'); setState(() { activeToast = ActiveToastType.none; toast = null; @@ -555,58 +431,24 @@ class _HomeScreenState extends FirkaState } Future _preloadImages() async { - final imagePaths = widget.data.settings.appIcons.keys + final imagePaths = initData.settings.appIcons.keys .map((icon) => "assets/images/icons/$icon.webp") .toList(); - imagePaths.add("assets/images/background.webp"); try { await ImagePreloader.preloadMultipleAssets(FirkaBundle(), imagePaths); - if (!mounted) return; - setState(() { - _preloadDone = true; - }); + setState(() => _preloadDone = true); } catch (e) { logger.severe('Home: error preloading images: $e'); if (!mounted) return; - setState(() { - _preloadDone = true; - }); + setState(() => _preloadDone = true); } } - void _onRefresh() async { - late void Function() finishListener; - finishListener = () { - widget.updateFinishedNotifier.removeListener(finishListener); - - _refreshController.refreshCompleted(); - }; - - widget.updateFinishedNotifier.addListener(finishListener); - widget.updateNotifier.update(); - } - - void _onLoading() async { - if (mounted) setState(() {}); - _refreshController.loadComplete(); - } - @override Widget build(BuildContext context) { - if (!widget.data.settings.group("settings").boolean("beta_warning")) { - Timer.run(() { - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute(builder: (context) => BetaScreen(widget.data)), - (route) => false, - ); - }); - - return SizedBox(); - } - if (!_preloadDone) { return Scaffold( backgroundColor: appStyle.colors.background, @@ -624,16 +466,8 @@ class _HomeScreenState extends FirkaState ); } - if (widget.watchPair && !pairingDone) { - Timer.run(() { - showWearBottomSheet(context, widget.data, widget.model ?? "unknown"); - - // pairingDone = true; - }); - } - if (_fetching) { - if (_disposed) return SizedBox(); + if (_disposed) return const SizedBox.shrink(); setState(() { activeToast = ActiveToastType.fetching; toast = Positioned( @@ -649,7 +483,6 @@ class _HomeScreenState extends FirkaState padding: EdgeInsets.all(8), child: Row( mainAxisSize: MainAxisSize.min, - // Use min to prevent filling the width children: [ SizedBox( width: 24, @@ -660,7 +493,7 @@ class _HomeScreenState extends FirkaState ), SizedBox(width: 16), Text( - widget.data.l10n.refreshing, + initData.l10n.refreshing, style: appStyle.fonts.B_16SB.copyWith( color: appStyle.colors.textPrimary, ), @@ -675,244 +508,13 @@ class _HomeScreenState extends FirkaState } SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - return DefaultAssetBundle( - bundle: FirkaBundle(), - child: PopScope( - canPop: canPop || subPageActive.value, - child: Scaffold( - backgroundColor: appStyle.colors.background, - body: SafeArea( - child: SizedBox( - height: MediaQuery.of(context).size.height, - child: Stack( - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: RefreshConfiguration( - springDescription: SpringDescription( - mass: 1.9, - stiffness: 85, - damping: 16, - ), - child: SmartScroll( - controller: _refreshController, - onLoading: _onLoading, - onRefresh: _onRefresh, - header: MaterialClassicHeader( - color: appStyle.colors.accent, - backgroundColor: appStyle.colors.background, - offset: 24, - ), - physics: ClampingScrollPhysics(), - child: PageView( - controller: _pageController, - physics: ClampingScrollPhysics(), - onPageChanged: (index) { - HapticFeedback.heavyImpact(); - - if (forcedNav > 0) { - forcedNav--; - - if (previousPages.isEmpty) { - canPop = true; - } - return; - } - - setState(() { - previousPages.add(homeScreenPage); - canPop = false; - homeScreenPage = HomePage.values[index]; - }); - }, - children: [ - HomeSubPage( - HomePage.home, - widget.data, - widget.updateNotifier, - widget.updateFinishedNotifier, - ), - HomeSubPage( - HomePage.grades, - widget.data, - widget.updateNotifier, - widget.updateFinishedNotifier, - ), - HomeSubPage( - HomePage.timetable, - widget.data, - widget.updateNotifier, - widget.updateFinishedNotifier, - ), - ], - ), - ), - ), - ), - Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - colors: [ - appStyle.colors.background, - appStyle.colors.background.withValues(alpha: 0.0), - ], - stops: const [0.0, 1.0], - ), - ), - width: MediaQuery.of(context).size.width, - child: Padding( - padding: const EdgeInsets.fromLTRB(55, 0, 55, 12), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // Home Button - BottomNavIconWidget( - () { - if (homeScreenPage != HomePage.home) { - if (previousPages.length > 1 && - forcedNavPage) { - forcedNavPage = false; - _pageController.jumpToPage( - previousPages[previousPages.length - 2] - .index, - ); - } - if (previousPages.length > 1 && - forcedNavPage) { - forcedNavPage = false; - _pageController.jumpToPage( - previousPages[previousPages.length - 2] - .index, - ); - } - _pageController.animateToPage( - HomePage.home.index, - duration: Duration(milliseconds: 175), - curve: Curves.easeInOut, - ); - } - }, - homeScreenPage == HomePage.home, - homeScreenPage == HomePage.home - ? Majesticon.homeSolid - : Majesticon.homeLine, - widget.data.l10n.home, - homeScreenPage == HomePage.home - ? appStyle.colors.accent - : appStyle.colors.secondary, - appStyle.colors.textPrimary, - ), - // Grades Button - BottomNavIconWidget( - () { - if (homeScreenPage != HomePage.grades) { - if (previousPages.length > 1 && - forcedNavPage) { - forcedNavPage = false; - _pageController.jumpToPage( - previousPages[previousPages.length - 2] - .index, - ); - } - _pageController.animateToPage( - HomePage.grades.index, - duration: Duration(milliseconds: 175), - curve: Curves.easeInOut, - ); - } - }, - homeScreenPage == HomePage.grades, - homeScreenPage == HomePage.grades - ? Majesticon.bookmarkSolid - : Majesticon.bookmarkLine, - widget.data.l10n.grades, - homeScreenPage == HomePage.grades - ? appStyle.colors.accent - : appStyle.colors.secondary, - appStyle.colors.textPrimary, - ), - // Timetable Button - BottomNavIconWidget( - () { - if (homeScreenPage != HomePage.timetable) { - if (previousPages.length > 1 && - forcedNavPage) { - forcedNavPage = false; - _pageController.jumpToPage( - previousPages[previousPages.length - 2] - .index, - ); - } - _pageController.animateToPage( - HomePage.timetable.index, - duration: Duration(milliseconds: 175), - curve: Curves.easeInOut, - ); - } - }, - homeScreenPage == HomePage.timetable, - homeScreenPage == HomePage.timetable - ? Majesticon.calendarSolid - : Majesticon.calendarLine, - widget.data.l10n.timetable, - homeScreenPage == HomePage.timetable - ? appStyle.colors.accent - : appStyle.colors.secondary, - appStyle.colors.textPrimary, - ), - // More Button - BottomNavIconWidget( - () { - HapticFeedback.lightImpact(); - showExtrasBottomSheet(context, widget.data); - }, - false, - widget.data.profilePicture != null - ? widget.data.profilePicture! - : Majesticon.menuLine, - widget.data.l10n.other, - appStyle.colors.secondary, - appStyle.colors.textPrimary, - isProfilePicture: - widget.data.profilePicture != null, - ), - ], - ), - ), - ), - ], - ), - toast ?? SizedBox(), - ], - ), - ), - ), + return Scaffold( + backgroundColor: appStyle.colors.background, + body: SafeArea( + child: SizedBox( + height: MediaQuery.of(context).size.height, + child: Stack(children: [widget.child, toast ?? SizedBox.shrink()]), ), - onPopInvokedWithResult: (bool didPop, dynamic result) { - if (subPageActive.value) { - subPageBack.update(); - return; - } - - if (previousPages.isNotEmpty && - homeScreenPage != previousPages.last) { - setState(() { - homeScreenPage = previousPages.removeLast(); - - forcedNav++; - _pageController.animateToPage( - homeScreenPage.index, - duration: Duration(milliseconds: 175), - curve: Curves.easeInOut, - ); - canPop = previousPages.isEmpty; - }); - } - }, ), ); } @@ -932,25 +534,18 @@ class _HomeScreenState extends FirkaState } } - bool _hasCompletedFirstPrefetch = false; - void _refreshLiveActivityOnResume() async { - if (!_hasCompletedFirstPrefetch) { - logger.info( - '[Home] Skipping LiveActivity update: first prefetch not yet complete', - ); - return; - } + if (!_hasCompletedFirstPrefetch) return; try { final token = pickActiveToken( - tokens: widget.data.tokens, - settings: widget.data.settings, + tokens: initData.tokens, + settings: initData.settings, ); final studentName = token?.studentId ?? "Student"; await LiveActivityService.checkAndUpdateTimetable( - client: widget.data.client, + client: initData.client, studentName: studentName, - settingsStore: widget.data.settings, + settingsStore: initData.settings, ); } catch (e) { logger.warning( @@ -962,13 +557,10 @@ class _HomeScreenState extends FirkaState @override void dispose() { WidgetsBinding.instance.removeObserver(this); - _pageController.dispose(); - widget.data.settingsUpdateNotifier.removeListener(settingsUpdateListener); - widget.data.profilePictureUpdateNotifier.removeListener( + initData.settingsUpdateNotifier.removeListener(settingsUpdateListener); + initData.profilePictureUpdateNotifier.removeListener( _onProfilePictureUpdated, ); - - // Remove reauth state listener KretaClient.reauthStateNotifier.removeListener(_onReauthStateChanged); _disposed = true; @@ -978,210 +570,3 @@ class _HomeScreenState extends FirkaState super.dispose(); } } - -class HomeSubPage extends StatefulWidget { - final HomePage page; - final AppInitialization data; - final UpdateNotifier _updateNotifier; - final UpdateNotifier _updateFinishNotifier; - - const HomeSubPage( - this.page, - this.data, - this._updateNotifier, - this._updateFinishNotifier, { - super.key, - }); - - @override - State createState() => _HomeSubPage(); -} - -class _HomeSubPage extends State { - HomePage? forcedHomePage; - String? subPageData; - - @override - void initState() { - super.initState(); - pageNavNotifier.addListener(forcePageNavHandler); - } - - @override - Widget build(BuildContext context) { - if (forcedHomePage != null) { - final p = forcedHomePage!; - if (subPageData == null) { - switch (p) { - case HomePage.home: - return HomeMainScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - ); - case HomePage.grades: - return PageWithSubPages( - [ - (cb) => HomeGradesScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - cb, - ), - (cb) => HomeGradesSubjectScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - cb, - ), - ], - subPageActive, - subPageBack, - pageIndex: 0, - ); - case HomePage.timetable: - return PageWithSubPages( - [ - (cb) => HomeTimetableScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - cb, - ), - (cb) => HomeTimetableMonthlyScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - cb, - ), - ], - subPageActive, - subPageBack, - pageIndex: 0, - ); - } - } else { - switch (p) { - case HomePage.home: - return HomeMainScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - ); - case HomePage.grades: - activeSubjectUid = subPageData!; - return PageWithSubPages( - [ - (cb) => HomeGradesSubjectScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - cb, - ), - (cb) => HomeGradesScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - cb, - ), - ], - subPageActive, - subPageBack, - pageIndex: 0, - ); - case HomePage.timetable: - return PageWithSubPages( - [ - (cb) => HomeTimetableMonthlyScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - cb, - ), - (cb) => HomeTimetableScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - cb, - ), - ], - subPageActive, - subPageBack, - pageIndex: 0, - ); - } - } - } - - switch (widget.page) { - case HomePage.home: - return HomeMainScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - ); - case HomePage.grades: - return PageWithSubPages( - [ - (cb) => HomeGradesScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - cb, - ), - (cb) => HomeGradesSubjectScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - cb, - ), - ], - subPageActive, - subPageBack, - pageIndex: 0, - ); - case HomePage.timetable: - return PageWithSubPages( - [ - (cb) => HomeTimetableScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - cb, - ), - (cb) => HomeTimetableMonthlyScreen( - widget.data, - widget._updateNotifier, - widget._updateFinishNotifier, - cb, - ), - ], - subPageActive, - subPageBack, - pageIndex: 0, - ); - } - } - - @override - void dispose() { - super.dispose(); - pageNavNotifier.removeListener(forcePageNavHandler); - } - - void forcePageNavHandler() { - if (!mounted) return; - - forcedNavPage = true; - setState(() { - forcedHomePage = pageNavNotifier.value.page; - subPageData = pageNavNotifier.value.subPageParams; - subjectName = pageNavNotifier.value.subjectName; - subjectId = pageNavNotifier.value.subjectId ?? ""; - subjectCategory = pageNavNotifier.value.subjectCategory ?? ""; - previousPages.add(homeScreenPage); - homeScreenPage = forcedHomePage!; - globalUpdate.update(); - }); - } -} diff --git a/firka/lib/ui/phone/screens/settings/settings_screen.dart b/firka/lib/ui/phone/screens/settings/settings_screen.dart index 7d019a8..c7c160d 100644 --- a/firka/lib/ui/phone/screens/settings/settings_screen.dart +++ b/firka/lib/ui/phone/screens/settings/settings_screen.dart @@ -9,10 +9,10 @@ import 'package:firka/ui/components/firka_button.dart'; import 'package:firka/ui/components/firka_card.dart'; import 'package:firka/app/app_state.dart'; import 'package:firka/ui/theme/style.dart'; -import 'package:firka/ui/phone/screens/login/login_screen.dart'; import 'package:firka/ui/shared/firka_icon.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:flutter/services.dart'; import 'package:isar_community/isar.dart'; import 'package:majesticons_flutter/majesticons_flutter.dart'; @@ -185,15 +185,7 @@ class _SettingsScreenState extends FirkaState { launchUrlString("https://firka.app/privacy"); return; } else { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => DefaultAssetBundle( - bundle: FirkaBundle(), - child: SettingsScreen(widget.data, item.children), - ), - ), - ); + context.push('/settings', extra: item.children); } }, child: item.redirectTo != null @@ -995,12 +987,7 @@ class _SettingsScreenState extends FirkaState { KretaClient.clearReauthFlag(); } if (!mounted) return; - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (context) => LoginScreen(widget.data), - ), - (route) => false, - ); + context.go('/login'); } else { if (Platform.isIOS) { final nextToken = accounts.first; diff --git a/firka/lib/ui/phone/widgets/login_webview.dart b/firka/lib/ui/phone/widgets/login_webview.dart index 042fd5f..1772dbb 100644 --- a/firka/lib/ui/phone/widgets/login_webview.dart +++ b/firka/lib/ui/phone/widgets/login_webview.dart @@ -13,12 +13,10 @@ import 'package:firka/services/watch_sync_helper.dart'; import 'package:firka/api/consts.dart'; import 'package:firka/api/token_grant.dart'; import 'package:firka/data/models/token_model.dart'; -import 'package:firka/core/firka_bundle.dart'; import 'package:firka/app/initialization_screen.dart'; import 'package:firka/core/state/firka_state.dart'; import 'package:firka/core/settings.dart'; import 'package:firka/ui/theme/style.dart'; -import '../pages/error/error_page.dart'; class LoginWebviewWidget extends StatefulWidget { final AppInitialization data; @@ -166,15 +164,7 @@ class _LoginWebviewWidgetState extends FirkaState } else { logger.shout("oauthredirect failed:", ex.toString()); } - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => DefaultAssetBundle( - bundle: FirkaBundle(), - child: ErrorPage(exception: ex.toString()), - ), - ), - ); + appRouter?.go('/error', extra: ex.toString()); } return NavigationDecision.prevent; diff --git a/firka/pubspec.yaml b/firka/pubspec.yaml index f69c7a8..76e35f6 100644 --- a/firka/pubspec.yaml +++ b/firka/pubspec.yaml @@ -51,6 +51,7 @@ dependencies: flutter_html: ^3.0.0 fl_chart: ^1.1.1 flutter_native_splash: ^2.4.7 + go_router: ^17.1.0 dev_dependencies: flutter_test: