diff --git a/lib/common/routing/app_route_information_parser.dart b/lib/common/routing/app_route_information_parser.dart deleted file mode 100644 index 686db2c..0000000 --- a/lib/common/routing/app_route_information_parser.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:uneconly/common/model/short_group_info.dart'; -import 'package:uneconly/common/routing/app_route_path.dart'; - -class AppRouteInformationParser extends RouteInformationParser { - @override - Future parseRouteInformation( - RouteInformation routeInformation, - ) async { - final uri = routeInformation.uri; - // Handle '/' - if (uri.pathSegments.isEmpty) { - return const AppRoutePath.loading(); - } - - // Handle '/select' - if (uri.pathSegments[0] == 'select') { - return const AppRoutePath.select(); - } - - // Handle '/settings' - if (uri.pathSegments[0] == 'settings') { - return const AppRoutePath.settings(); - } - - // Handle '/group/:id/schedule/' - if (uri.pathSegments.length == 3) { - if (uri.pathSegments[0] != 'group') { - return const AppRoutePath.loading(); - } - - var groupIdPath = uri.pathSegments[1]; - var id = int.tryParse(groupIdPath); - var name = uri.queryParameters['name']; - - if (id == null) { - return const AppRoutePath.select(); - } - - if (uri.pathSegments[2] != 'schedule') { - return const AppRoutePath.select(); - } - - return AppRoutePath.schedule( - shortGroupInfo: ShortGroupInfo( - groupId: id, - groupName: name, - ), - ); - } - - // Handle unknown routes - return const AppRoutePath.select(); - } - - @override - RouteInformation restoreRouteInformation(AppRoutePath configuration) { - return configuration.map( - loading: (configuration) { - return RouteInformation( - uri: Uri.parse('/'), - state: configuration.toJson(), - ); - }, - schedule: (configuration) { - return RouteInformation( - uri: Uri.parse( - '/group/${configuration.shortGroupInfo.groupId}/schedule?name=${configuration.shortGroupInfo.groupName}', - ), - state: configuration.toJson(), - ); - }, - select: (configuration) { - return RouteInformation( - uri: Uri.parse('/select'), - state: configuration.toJson(), - ); - }, - settings: (configuration) { - return RouteInformation( - uri: Uri.parse('/settings'), - state: configuration.toJson(), - ); - }, - ); - } -} diff --git a/lib/common/routing/app_route_path.dart b/lib/common/routing/app_route_path.dart deleted file mode 100644 index 884423d..0000000 --- a/lib/common/routing/app_route_path.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:uneconly/common/model/short_group_info.dart'; - -part 'app_route_path.freezed.dart'; -part 'app_route_path.g.dart'; - -/// AppRoutePath data class -@freezed -class AppRoutePath with _$AppRoutePath { - const factory AppRoutePath.select({ - final ShortGroupInfo? shortGroupInfo, - }) = SelectAppRoutePath; - - const factory AppRoutePath.schedule({ - required final ShortGroupInfo shortGroupInfo, - }) = ScheduleAppRoutePath; - - const factory AppRoutePath.loading() = LoadingAppRoutePath; - - const factory AppRoutePath.settings() = SettingsAppRoutePath; - - const AppRoutePath._(); - - /// Generate AppRoutePath class from Map - factory AppRoutePath.fromJson(Map json) => - _$AppRoutePathFromJson(json); -} diff --git a/lib/common/routing/app_router.dart b/lib/common/routing/app_router.dart deleted file mode 100644 index 3d10449..0000000 --- a/lib/common/routing/app_router.dart +++ /dev/null @@ -1,256 +0,0 @@ -// ignore_for_file: avoid-returning-widgets - -import 'package:flutter/material.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:l/l.dart'; -import 'package:uneconly/common/routing/app_route_path.dart'; -import 'package:uneconly/common/routing/app_router_delegate.dart'; -import 'package:uneconly/common/routing/navigation_observer.dart'; - -typedef NavigateCallback = AppRoutePath Function(AppRoutePath configuration); - -/// Скоуп для управления роутингом и навигацией приложения -@immutable -class AppRouter extends InheritedNotifier { - const AppRouter({ - required Widget child, - required AppRouterDelegate routerDelegate, - Key? key, - }) : _routerDelegate = routerDelegate, - super( - key: key, - child: child, - notifier: routerDelegate, - ); - - final AppRouterDelegate _routerDelegate; - - AppRouterDelegate get router => _routerDelegate; - - NavigatorState? get navigator => router.pageObserver.navigator; - - @doNotStore - static AppRouter of(BuildContext context, {bool listen = false}) { - AppRouter? appRouter; - if (listen) { - appRouter = context.dependOnInheritedWidgetOfExactType(); - } else { - final inhW = - context.getElementForInheritedWidgetOfExactType()?.widget; - appRouter = inhW is AppRouter ? inhW : null; - } - - return appRouter ?? _notInScope(); - } - - static Never _notInScope() => - throw UnsupportedError('Not in AppRouter scope'); - - /// Получить навигатор из контекста - /// См также [NavigatorState] - static NavigatorState? navigatorOf(BuildContext context) => - of(context, listen: false).navigator; - - /// Получить роутер из контекста - /// См также [RouterDelegate] - static AppRouterDelegate routerOf(BuildContext context) => - of(context, listen: false).router; - - /// Получить обозреватель страниц из контекста - /// См также [RouteObserver], [NavigatorObserver] - static PageObserver pageObserverOf(BuildContext context) => - of(context, listen: false).router.pageObserver; - - /// Получить обозреватель страниц из контекста - /// См также [RouteObserver], [NavigatorObserver], [RouteAware] - static ModalObserver modalObserverOf(BuildContext context) => - of(context, listen: false).router.modalObserver; - - /// Можно ли закрыть текущий роут - /// См также [Navigator.canPop], [ModalRoute.canPop] - static bool canPop(BuildContext context, {bool listen = false}) => listen - ? (ModalRoute.of(context)?.canPop ?? false) - : (navigatorOf(context)?.canPop() ?? false); - - /// Попробывать закрыть текущий роут - /// См также [Navigator.maybePop] - static Future maybePop( - BuildContext context, [ - T? result, - ]) { - l.i('Попробуем вернуться к предидущему роуту'); - - return navigatorOf(context)?.maybePop(result) ?? - Future.value(false); - } - - /// Вернуться назад, а в случае неудачи - на начальный экран - static void popOrHome(BuildContext context) => maybePop(context).then( - (value) { - if (!value) { - goHome(context); - } - }, - ); - - /// Обновить конфигурацию роутера и перейти на новую страницу - /// См также [Router.neglect], [Router.navigate] - static void navigate( - BuildContext context, - NavigateCallback callback, { - NavigateMode mode = NavigateMode.auto, - }) { - l.i('Обновить конфигурацию роутера'); - final delegate = of(context, listen: false).router; - switch (mode) { - case NavigateMode.force: - // Перейдем на новую страницу и создадим запись в истории браузера - Router.navigate( - context, - () => delegate.setNewRoutePath( - callback(delegate.currentConfiguration), - ), - ); - break; - case NavigateMode.neglect: - // Перейдем на новую страницу без создания новой записи в истории браузера - Router.neglect( - context, - () => delegate.setNewRoutePath( - callback(delegate.currentConfiguration), - ), - ); - break; - case NavigateMode.auto: - default: - delegate.setNewRoutePath( - callback(delegate.currentConfiguration), - ); - break; - } - } - - /// Обновить конфигурацию роутера и перейти на новую страницу - /// См также [RouterDelegate.popRoute] - static Future pop(BuildContext context) { - l.i('Вернемся к предидущему роуту'); - - return of(context, listen: false).router.popRoute(); - } - - /// Перейти на начальную страницу - static void goHome( - BuildContext context, { - NavigateMode mode = NavigateMode.neglect, - }) => - navigate( - context, - (_) => const AppRoutePath.select(), - mode: mode, - ); - - /// Получить текущие аргументы роута - static Object? routeArguments(BuildContext context) => - ModalRoute.of(context)?.settings.arguments; - - /// Содержится ли [AppRouter] в переданном контексте - static bool containedIn(BuildContext context) => - context.getElementForInheritedWidgetOfExactType() != null; - - /// Добавить в навигатор анонимный экран. - /// ВНИМАНИЕ! Для основных экранов стоит отдать предпочтение [navigate] - /// [Navigator.push] - static Future push( - BuildContext context, - WidgetBuilder builder, { - String? name, - Object? arguments, - }) { - l.i('Перейдем на новый анонимный роут'); - - return navigatorOf(context)?.push( - MaterialPageRoute( - builder: builder, - settings: RouteSettings( - name: name, - arguments: arguments, - ), - ), - ) ?? - Future.value(null); - } - - /// Отобразить диалог от [AppRouter] - /// См также [showDialog] - static Future showModalDialog( - BuildContext context, - WidgetBuilder builder, { - RouteSettings? routeSettings, - }) { - l.i('Отобразим ModalDialog'); - final navigator = navigatorOf(context); - if (navigator == null) return Future.value(null); - - return showDialog( - context: navigator.context, - builder: builder, - useRootNavigator: false, - routeSettings: routeSettings, - barrierDismissible: true, - useSafeArea: true, - barrierColor: Colors.black54, - ); - } - - /// Shortcut to showModalBottomSheet() - static Future showBottomSheet({ - required BuildContext context, - required WidgetBuilder builder, - Color? backgroundColor, - double? elevation, - ShapeBorder? shape, - Clip? clipBehavior, - Color? barrierColor, - bool isScrollControlled = true, - bool isDismissible = true, - bool enableDrag = true, - RouteSettings? routeSettings, - }) { - l.i('Отобразим ModalBottomSheet'); - - return showModalBottomSheet( - context: navigatorOf(context)!.context, - builder: builder, - backgroundColor: backgroundColor, - elevation: elevation, - shape: shape, - clipBehavior: clipBehavior, - barrierColor: barrierColor, - isScrollControlled: isScrollControlled, - isDismissible: isDismissible, - enableDrag: enableDrag, - //routeSettings: routeSettings, - ); - } - - static void showLicensePageOf(BuildContext context) => showLicensePage( - context: navigatorOf(context)!.context, - // applicationIcon: const DartLogoIcon(), - // applicationName: context.localization.title, - // applicationVersion: pubspec.version, - applicationLegalese: '@smart7even', - useRootNavigator: true, - ); -} - -/// Управляет режимом навигации [AppRouter.navigate] -enum NavigateMode { - /// Создает новую запись если URL отличается - auto, - - /// Принудительно создает новую запись в истории браузера - force, - - /// Принудительно не создает новую запись в истории браузера - neglect, -} diff --git a/lib/common/routing/app_router_delegate.dart b/lib/common/routing/app_router_delegate.dart deleted file mode 100644 index c05df05..0000000 --- a/lib/common/routing/app_router_delegate.dart +++ /dev/null @@ -1,195 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:uneconly/common/model/short_group_info.dart'; -import 'package:uneconly/common/routing/app_route_path.dart'; -import 'package:uneconly/common/routing/app_router.dart'; -import 'package:uneconly/common/routing/navigation_observer.dart'; -import 'package:uneconly/feature/loading/widget/loading_page.dart'; -import 'package:uneconly/feature/schedule/widget/schedule_page.dart'; -import 'package:uneconly/feature/select/widget/select_page.dart'; -import 'package:uneconly/feature/settings/widget/settings_page.dart'; - -class AppRouterDelegate extends RouterDelegate - with ChangeNotifier, PopNavigatorRouterDelegateMixin { - @override - final GlobalKey navigatorKey; - - AppRouterDelegate() - : navigatorKey = GlobalKey(), - pageObserver = PageObserver(), - modalObserver = ModalObserver(); - - final PageObserver pageObserver; - final ModalObserver modalObserver; - - final List _history = []; - AppRoutePath currentPath = const AppRoutePath.loading(); - - void handleSchedulePageOpened(int groupId, String groupName) { - currentPath = AppRoutePath.schedule( - shortGroupInfo: ShortGroupInfo( - groupId: groupId, - groupName: groupName, - ), - ); - notifyListeners(); - } - - @override - Widget build(BuildContext context) { - AppRoutePath path = currentPath; - - return AppRouter( - routerDelegate: this, - child: Navigator( - key: navigatorKey, - pages: path.map( - loading: (value) { - return [ - const MaterialPage( - key: ValueKey('LoadingPage'), - child: LoadingPage( - isActive: true, - ), - ), - ]; - }, - schedule: (path) { - return [ - MaterialPage( - key: const ValueKey('SchedulePage'), - child: SchedulePage( - shortGroupInfo: ShortGroupInfo( - groupId: path.shortGroupInfo.groupId, - groupName: path.shortGroupInfo.groupName, - ), - isViewMode: false, - ), - ), - ]; - }, - select: (path) { - final shortGroupInfo = path.shortGroupInfo; - - return [ - if (shortGroupInfo != null) - MaterialPage( - key: const ValueKey('SchedulePage'), - child: SchedulePage( - shortGroupInfo: ShortGroupInfo( - groupId: shortGroupInfo.groupId, - groupName: shortGroupInfo.groupName, - ), - isViewMode: false, - ), - ), - const MaterialPage( - key: ValueKey('SelectPage'), - child: SelectPage( - mode: SelectPageMode.select, - ), - ), - ]; - }, - settings: (SettingsAppRoutePath path) { - AppRoutePath? previousPath; - - if (_history.isNotEmpty) { - previousPath = _history.lastWhereOrNull( - (element) => element is ScheduleAppRoutePath, - ); - } else { - previousPath = null; - } - - if (previousPath is ScheduleAppRoutePath) { - return [ - MaterialPage( - key: const ValueKey('SchedulePage'), - child: SchedulePage( - shortGroupInfo: ShortGroupInfo( - groupId: previousPath.shortGroupInfo.groupId, - groupName: previousPath.shortGroupInfo.groupName, - ), - isViewMode: false, - ), - ), - const MaterialPage( - key: ValueKey('SettingsPage'), - child: SettingsPage(), - ), - ]; - } - - return [ - const MaterialPage( - key: ValueKey('LoadingPage'), - child: LoadingPage( - isActive: false, - ), - ), - const MaterialPage( - key: ValueKey('SettingsPage'), - child: SettingsPage(), - ), - ]; - }, - ), - onPopPage: (route, result) { - if (!route.didPop(result)) { - return false; - } - - currentPath.mapOrNull( - select: (value) { - var shortGroupInfo = value.shortGroupInfo; - if (shortGroupInfo != null) { - currentPath = AppRoutePath.schedule( - shortGroupInfo: shortGroupInfo, - ); - } - }, - settings: (value) { - AppRoutePath? previousPath; - - if (_history.isNotEmpty) { - previousPath = _history.lastWhereOrNull( - (element) => element is ScheduleAppRoutePath, - ); - } else { - previousPath = null; - } - - if (previousPath is ScheduleAppRoutePath) { - currentPath = AppRoutePath.schedule( - shortGroupInfo: ShortGroupInfo( - groupId: previousPath.shortGroupInfo.groupId, - groupName: previousPath.shortGroupInfo.groupName, - ), - ); - } else { - currentPath = const AppRoutePath.loading(); - } - }, - ); - - notifyListeners(); - - return true; - }, - ), - ); - } - - @override - Future setNewRoutePath(AppRoutePath configuration) async { - currentPath = configuration; - _history.add(configuration); - notifyListeners(); - } - - @override - AppRoutePath get currentConfiguration { - return currentPath; - } -}