import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_rx_bloc/flutter_rx_bloc.dart'; import 'package:hub/features/backend/index.dart'; import 'package:hub/features/documents/documents.dart'; import 'package:hub/features/history/index.dart'; import 'package:hub/features/home/index.dart'; import 'package:hub/features/local/index.dart'; import 'package:hub/features/menu/index.dart'; import 'package:hub/features/property/index.dart'; import 'package:hub/features/storage/index.dart'; import 'package:hub/flutter_flow/flutter_flow_util.dart'; import 'package:hub/flutter_flow/nav/nav.dart'; import 'package:hub/pages/delivery_schedule_page/delivery_schedule_widget.dart'; import 'package:hub/pages/fast_pass_page/fast_pass_page_widget.dart'; import 'package:hub/pages/forgot_password_page/forgot_password_screen.dart'; import 'package:hub/pages/liberation_history/liberation_history_widget.dart'; import 'package:hub/pages/message_history_page/message_history_page_widget.dart'; import 'package:hub/pages/package_order_page/package_order_page.dart'; import 'package:hub/pages/people_on_the_property_page/people_on_the_property_page_widget.dart'; import 'package:hub/pages/pets_on_the_property_page/pets_history_screen.dart'; import 'package:hub/pages/pets_page/pets_page_widget.dart'; import 'package:hub/pages/preferences_settings_page/preferences_settings_widget.dart'; import 'package:hub/pages/provisional_schedule_page/provisional_schedule_widget.dart'; import 'package:hub/pages/qr_code_page/qr_code_page_widget.dart'; import 'package:hub/pages/reception_page/reception_page_widget.dart'; import 'package:hub/pages/register_visitor_page/register_visitor_page_widget.dart'; import 'package:hub/pages/reservation_page/reservation_page_widget.dart'; import 'package:hub/pages/residents_on_the_property/residents_on_the_property_screen.dart'; import 'package:hub/pages/schedule_complete_visit_page/schedule_complete_visit_page_widget.dart'; import 'package:hub/pages/sign_in_page/sign_in_page_widget.dart'; import 'package:hub/pages/sign_up_page/sign_up_page_widget.dart'; import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart'; import 'package:hub/pages/visits_on_the_property/visits_on_the_property_screen.dart'; import 'package:hub/pages/welcome_page/welcome_page_widget.dart'; import 'package:hub/shared/utils/dialog_util.dart'; import 'package:provider/provider.dart'; export 'package:go_router/go_router.dart'; export 'serialization_util.dart'; const kTransitionInfoKey = '__transition_info__'; final GlobalKey navigatorKey = GlobalKey(); class AppStateNotifier extends ChangeNotifier { AppStateNotifier._(); static AppStateNotifier? _instance; static AppStateNotifier get instance => _instance ??= AppStateNotifier._(); bool showSplashImage = true; void stopShowingSplashImage() { showSplashImage = false; notifyListeners(); } } GoRouter createRouter(AppStateNotifier appStateNotifier) { return GoRouter( navigatorKey: navigatorKey, initialLocation: '/', debugLogDiagnostics: true, redirect: (context, state) { if (Platform.isIOS) { if (state.uri.toString().contains("freaccess://changepass/")) { throw Exception('Redirecting to forgotPassword'); } } return null; }, refreshListenable: appStateNotifier, // errorBuilder: (context, state) => appStateNotifier.showSplashImage // ? Builder( builder: (context) => Container(color: FlutterFlowTheme.of(context).primary, child: Image.asset( 'assets/images/logo.svg', fit: BoxFit.cover))) // : const PeopleOnThePropertyPageWidget(), routes: [ // FFRoute(name: '_initialize', path: '/',builder: (context, _) => appStateNotifier.showSplashImage // ? Builder(builder: (context) => Container(color: FlutterFlowTheme.of(context).primary, child: Image.asset('assets/images/favicon.png', fit: BoxFit.cover))) // : const OnBoardingPageWidget(), // ), FFRoute( name: '_initialize', path: '/', builder: (context, _) { return FutureBuilder( future: () async { final bool isLogged = await StorageHelper().get(SecureStorageKey.isLogged.value) == 'true'; final bool haveLocal = await StorageHelper().get(SecureStorageKey.haveLocal.value) == 'true'; final bool haveUserUUID = (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?.isNotEmpty ?? false; final bool haveDevUUID = (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?.isNotEmpty ?? false; if (isLogged && haveDevUUID && haveUserUUID) { return haveLocal ? MultiBlocProvider( providers: [ BlocProvider( create: (context) => MenuBloc( style: MenuView.list_grid, item: EnumMenuItem.button, entries: MenuEntry.getEntriesByType( MenuEntryType.Home), )..add(MenuEvent()), ), BlocProvider( create: (context) => HomeBloc()..add(HomeEvent()), ), BlocProvider( create: (context) => LocalProfileBloc()..add(LocalProfileEvent()), ), ], child: HomePageWidget( key: UniqueKey(), LocalsRepositoryImpl().update), ) : const ReceptionPageWidget(); } else { return const WelcomePage(); } }(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { DialogUtil.error(context, snapshot.error.toString()); return const WelcomePage(); } else { return snapshot.data!; } }, ); }, ), FFRoute( name: 'forgotPassword', path: '/forgotPassword', builder: (context, params) { late final String email = params.getParam('email', ParamType.String); late final String token = params.getParam('token', ParamType.String); return ForgotPasswordScreen( key: ValueKey('ForgotPasswordScreen'), email: email, token: token, ); }), FFRoute( name: 'aboutSystemPage', path: '/aboutSystemPage', builder: (context, params) => AboutSystemPage(), ), FFRoute( name: 'homePage', path: '/homePage', builder: (context, params) { final Future Function(BuildContext context)? update = params.getParam('update', ParamType.Function); return MultiBlocProvider( providers: [ BlocProvider( create: (context) => MenuBloc( style: MenuView.list_grid, item: EnumMenuItem.button, entries: MenuEntry.getEntriesByType(MenuEntryType.Home), )..add(MenuEvent()), ), BlocProvider( create: (context) => HomeBloc()..add(HomeEvent()), ), BlocProvider( create: (context) => LocalProfileBloc()..add(LocalProfileEvent()), ), ], child: HomePageWidget(key: UniqueKey(), update), ); }), FFRoute( name: 'petsOnThePropertyPage', path: '/petsOnThePropertyPage', builder: (context, params) => Scaffold(body: const PetsHistoryScreen(isApp: true))), FFRoute( name: 'vehiclesOnThePropertyPage', path: '/vehiclesOnThePropertyPage', builder: (context, params) => const VehiclePage()), FFRoute( name: 'receptionPage', path: '/receptionPage', builder: (context, params) => const ReceptionPageWidget()), FFRoute( name: 'messageHistoryPage', path: '/messageHistoryPage', builder: (context, params) => const MessageHistoryPageWidget()), FFRoute( name: 'registerVisitorPage', path: '/registerVisitorPage', builder: (context, params) => const RegisterVisitorPageWidget()), FFRoute( name: 'scheduleCompleteVisitPage', path: '/scheduleCompleteVisitPage', builder: (context, params) => const ScheduleCompleteVisitPageWidget()), FFRoute( name: 'deliverySchedule', path: '/deliverySchedule', builder: (context, params) => const DeliverySchedule()), FFRoute( name: 'provisionalSchedule', path: '/provisionalSchedule', builder: (context, params) => const ProvisionalSchedule()), FFRoute( name: 'fastPassPage', path: '/fastPassPage', builder: (context, params) => FastPassPageWidget()), FFRoute( name: 'preferencesSettings', path: '/preferencesSettings', builder: (context, params) => PreferencesPageWidget()), FFRoute( name: 'aboutProperty', path: '/aboutProperty', builder: (context, params) => AboutPropertyPage()), FFRoute( name: 'residentsOnThePropertyPage', path: '/residentsOnThePropertyPage', builder: (context, params) => ResidentsOnTheProperty()), FFRoute( name: 'visitsOnThePropertyPage', path: '/visitsOnThePropertyPage', builder: (context, params) => VisitsOnTheProperty()), FFRoute( name: 'peopleOnThePropertyPage', path: '/peopleOnThePropertyPage', builder: (context, params) => PeopleOnThePropertyPage()), FFRoute( name: 'acessHistoryPage', path: '/acessHistoryPage', builder: (context, params) => AccessHistoryScreen(opt: const { 'personType': '.*', 'accessType': '.*', 'search': '.*' })), FFRoute( name: 'provisionalHistoryPage', path: '/provisionalHistoryPage', builder: (context, params) => ProvisionalHistoryPage()), FFRoute( name: 'liberationHistory', path: '/liberationHistory', builder: (context, params) => const LiberationHistoryWidget()), FFRoute( name: 'signInPage', path: '/signInPage', builder: (context, params) => const SignInPageWidget()), FFRoute( name: 'signUpPage', path: '/signUpPage', builder: (context, params) => const SignUpPageWidget()), FFRoute( name: 'welcomePage', path: '/welcomePage', builder: (context, params) => const WelcomePage()), FFRoute( name: 'qrCodePage', path: '/qrCodePage', builder: (context, params) => const QrCodePageWidget()), FFRoute( name: 'preferencesPage', path: '/preferencesPage', builder: (context, params) => PreferencesPageWidget()), FFRoute( name: 'packageOrder', path: '/packageOrder', builder: (context, params) => const PackageOrderPage()), FFRoute( name: 'reservation', path: '/reservation', builder: (context, params) => ReservationPageWidget()), FFRoute( name: 'petsPage', path: '/petsPage', builder: (context, params) { final pet = params.getParam('pet', ParamType.JSON); return PetsPageWidget(pet: pet); }, ), FFRoute( name: 'documentPage', path: '/documentPage', builder: (context, params) { return RxBlocProvider( create: (context) => DocumentPageBloc(context), child: DocumentPage(), ); }, ), // FFRoute( // name: 'documentViewerScreen', // path: '/documentViewerScreen', // builder: (context, params) { // final Document doc = params.getParam('doc', ParamType.Function); // final Uri uri = params.getParam('uri', ParamType.Function); // return DocumentViewScreen( // key: UniqueKey(), // doc: (doc, // uri: uri, // ); // }, // ), // FFRoute(name: 'settingsPage', path: '/settingsPage', builder: (context, params) => params.isEmpty ? const NavBarPage(initialPage: 'settingsPage') : const SettingsPageWidget()) ].map((r) => r.toRoute(appStateNotifier)).toList(), ); } extension NavParamExtensions on Map { Map get withoutNulls => Map.fromEntries( entries .where((e) => e.value != null) .map((e) => MapEntry(e.key, e.value!)), ); } extension NavigationExtensions on BuildContext { void safePop() { if (canPop()) pop(); else go('/'); } } extension _GoRouterStateExtensions on GoRouterState { Map get extraMap => extra != null ? extra as Map : {}; Map get allParams => {} ..addAll(pathParameters) ..addAll(uri.queryParameters) ..addAll(extraMap); TransitionInfo get transitionInfo => extraMap.containsKey(kTransitionInfoKey) ? extraMap[kTransitionInfoKey] as TransitionInfo : TransitionInfo.appDefault(); } extension GoRouterLocationExtension on GoRouter { String getCurrentLocation() { final RouteMatch lastMatch = routerDelegate.currentConfiguration.last; final RouteMatchList matchList = lastMatch is ImperativeRouteMatch ? lastMatch.matches : routerDelegate.currentConfiguration; return matchList.uri.toString(); } } class FFParameters { FFParameters(this.state, [this.asyncParams = const {}]); final GoRouterState state; final Map Function(String)> asyncParams; Map futureParamValues = {}; bool get isEmpty => state.allParams.isEmpty || (state.allParams.length == 1 && state.extraMap.containsKey(kTransitionInfoKey)); bool isAsyncParam(MapEntry param) => asyncParams.containsKey(param.key) && param.value is String; bool get hasFutures => state.allParams.entries.any(isAsyncParam); Future completeFutures() => Future.wait( state.allParams.entries.where(isAsyncParam).map( (param) async { final doc = await asyncParams[param.key]!(param.value) .onError((_, __) => null); if (doc != null) { futureParamValues[param.key] = doc; return true; } else return false; }, ), ).onError((_, __) => [false]).then((v) => v.every((e) => e)); dynamic getParam(String paramName, ParamType type, {bool isList = false, StructBuilder? structBuilder}) { if (futureParamValues.containsKey(paramName)) return futureParamValues[paramName]; if (!state.allParams.containsKey(paramName)) return null; final param = state.allParams[paramName]; if (param is! String) return param; return deserializeParam(param, type, isList, structBuilder: structBuilder); } } class FFRoute { const FFRoute({ required this.name, required this.path, required this.builder, this.requireAuth = false, this.asyncParams = const {}, this.routes = const [], }); final String name; final String path; final bool requireAuth; final Map Function(String)> asyncParams; final Widget Function(BuildContext, FFParameters) builder; final List routes; GoRoute toRoute(AppStateNotifier appStateNotifier) => GoRoute( name: name, path: path, pageBuilder: (context, state) { fixStatusBarOniOS16AndBelow(context); final ffParams = FFParameters(state, asyncParams); final page = ffParams.hasFutures ? FutureBuilder( future: ffParams.completeFutures(), builder: (context, _) => builder(context, ffParams), ) : builder(context, ffParams); final child = page; final transitionInfo = state.transitionInfo; return transitionInfo.hasTransition ? CustomTransitionPage( key: state.pageKey, child: child, transitionDuration: transitionInfo.duration, transitionsBuilder: (context, animation, secondaryAnimation, child) => PageTransition( type: transitionInfo.transitionType, duration: transitionInfo.duration, reverseDuration: transitionInfo.duration, alignment: transitionInfo.alignment, child: child, ).buildTransitions( context, animation, secondaryAnimation, child), ) : MaterialPage(key: state.pageKey, child: child); }, routes: routes, ); } class TransitionInfo { const TransitionInfo({ required this.hasTransition, this.transitionType = PageTransitionType.fade, this.duration = const Duration(milliseconds: 300), this.alignment, }); final bool hasTransition; final PageTransitionType transitionType; final Duration duration; final Alignment? alignment; static TransitionInfo appDefault() => const TransitionInfo(hasTransition: false); } class RootPageContext { const RootPageContext(this.isRootPage, [this.errorRoute]); final bool isRootPage; final String? errorRoute; static bool isInactiveRootPage(BuildContext context) { final rootPageContext = context.read(); final isRootPage = rootPageContext?.isRootPage ?? false; final location = GoRouterState.of(context).uri.toString(); return isRootPage && location != '/' && location != rootPageContext?.errorRoute; } static Widget wrap(Widget child, {String? errorRoute}) => Provider.value(value: RootPageContext(true, errorRoute), child: child); }