nav.dart 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. import 'dart:async';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_spinkit/flutter_spinkit.dart';
  4. import 'package:go_router/go_router.dart';
  5. import 'package:page_transition/page_transition.dart';
  6. import 'package:provider/provider.dart';
  7. import '/backend/schema/structs/index.dart';
  8. import '/auth/custom_auth/custom_auth_user_provider.dart';
  9. import '/main.dart';
  10. import '/flutter_flow/flutter_flow_theme.dart';
  11. import '/flutter_flow/lat_lng.dart';
  12. import '/flutter_flow/place.dart';
  13. import '/flutter_flow/flutter_flow_util.dart';
  14. import 'serialization_util.dart';
  15. import '/index.dart';
  16. export 'package:go_router/go_router.dart';
  17. export 'serialization_util.dart';
  18. const kTransitionInfoKey = '__transition_info__';
  19. GlobalKey<NavigatorState> appNavigatorKey = GlobalKey<NavigatorState>();
  20. const debugRouteLinkMap = {
  21. '/login':
  22. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=login',
  23. '/horecagelegenheidCurrent':
  24. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=horecagelegenheidCurrent',
  25. '/home':
  26. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=home',
  27. '/selectprovinciegemeente':
  28. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=selectprovinciegemeente',
  29. '/pUitgaanPage':
  30. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=PUitgaanPage',
  31. '/event':
  32. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=Event',
  33. '/eventCurrent':
  34. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=EventCurrent',
  35. '/horecagelegenhedenOverzichtProvinciePage':
  36. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=HorecagelegenhedenOverzichtProvinciePage',
  37. '/wachtwoordVergeten':
  38. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=wachtwoordVergeten',
  39. '/favorieten':
  40. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=favorieten',
  41. '/kanwegTestUpload':
  42. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=kanwegTestUpload',
  43. '/mijnProfiel':
  44. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=mijnProfiel',
  45. '/stadsactiviteitAanmaken':
  46. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=stadsactiviteitAanmaken',
  47. '/uitgaansevenementAanmaken':
  48. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=uitgaansevenementAanmaken',
  49. '/horecagelegenhedenOverzichtCurrent':
  50. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=horecagelegenhedenOverzichtCurrent',
  51. '/thuisBezorgen':
  52. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=thuisBezorgen',
  53. '/favorietenCopy':
  54. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=favorietenCopy',
  55. '/favorietenCopy2':
  56. 'https://app.flutterflow.io/project/uitgaanskrant-1qhvtd?tab=uiBuilder&page=favorietenCopy2'
  57. };
  58. class AppStateNotifier extends ChangeNotifier {
  59. AppStateNotifier._();
  60. static AppStateNotifier? _instance;
  61. static AppStateNotifier get instance => _instance ??= AppStateNotifier._();
  62. UitgaanskrantAuthUser? initialUser;
  63. UitgaanskrantAuthUser? user;
  64. bool showSplashImage = true;
  65. String? _redirectLocation;
  66. /// Determines whether the app will refresh and build again when a sign
  67. /// in or sign out happens. This is useful when the app is launched or
  68. /// on an unexpected logout. However, this must be turned off when we
  69. /// intend to sign in/out and then navigate or perform any actions after.
  70. /// Otherwise, this will trigger a refresh and interrupt the action(s).
  71. bool notifyOnAuthChange = true;
  72. bool get loading => user == null || showSplashImage;
  73. bool get loggedIn => user?.loggedIn ?? false;
  74. bool get initiallyLoggedIn => initialUser?.loggedIn ?? false;
  75. bool get shouldRedirect => loggedIn && _redirectLocation != null;
  76. String getRedirectLocation() => _redirectLocation!;
  77. bool hasRedirect() => _redirectLocation != null;
  78. void setRedirectLocationIfUnset(String loc) => _redirectLocation ??= loc;
  79. void clearRedirectLocation() => _redirectLocation = null;
  80. /// Mark as not needing to notify on a sign in / out when we intend
  81. /// to perform subsequent actions (such as navigation) afterwards.
  82. void updateNotifyOnAuthChange(bool notify) => notifyOnAuthChange = notify;
  83. void update(UitgaanskrantAuthUser newUser) {
  84. final shouldUpdate =
  85. user?.uid == null || newUser.uid == null || user?.uid != newUser.uid;
  86. initialUser ??= newUser;
  87. user = newUser;
  88. // Refresh the app on auth change unless explicitly marked otherwise.
  89. // No need to update unless the user has changed.
  90. if (notifyOnAuthChange && shouldUpdate) {
  91. notifyListeners();
  92. }
  93. // Once again mark the notifier as needing to update on auth change
  94. // (in order to catch sign in / out events).
  95. updateNotifyOnAuthChange(true);
  96. }
  97. void stopShowingSplashImage() {
  98. showSplashImage = false;
  99. notifyListeners();
  100. }
  101. }
  102. GoRouter createRouter(AppStateNotifier appStateNotifier) => GoRouter(
  103. initialLocation: '/',
  104. debugLogDiagnostics: true,
  105. refreshListenable: appStateNotifier,
  106. navigatorKey: appNavigatorKey,
  107. errorBuilder: (context, state) =>
  108. appStateNotifier.loggedIn ? HomeWidget() : HomeWidget(),
  109. routes: [
  110. FFRoute(
  111. name: '_initialize',
  112. path: '/',
  113. builder: (context, _) =>
  114. appStateNotifier.loggedIn ? HomeWidget() : HomeWidget(),
  115. ),
  116. FFRoute(
  117. name: LoginWidget.routeName,
  118. path: LoginWidget.routePath,
  119. builder: (context, params) => LoginWidget(),
  120. ),
  121. FFRoute(
  122. name: HorecagelegenheidCurrentWidget.routeName,
  123. path: HorecagelegenheidCurrentWidget.routePath,
  124. builder: (context, params) => HorecagelegenheidCurrentWidget(
  125. nid: params.getParam(
  126. 'nid',
  127. ParamType.String,
  128. ),
  129. ),
  130. ),
  131. FFRoute(
  132. name: HomeWidget.routeName,
  133. path: HomeWidget.routePath,
  134. builder: (context, params) => HomeWidget(),
  135. ),
  136. FFRoute(
  137. name: SelectprovinciegemeenteWidget.routeName,
  138. path: SelectprovinciegemeenteWidget.routePath,
  139. builder: (context, params) => SelectprovinciegemeenteWidget(),
  140. ),
  141. FFRoute(
  142. name: PUitgaanPageWidget.routeName,
  143. path: PUitgaanPageWidget.routePath,
  144. builder: (context, params) => PUitgaanPageWidget(
  145. plaats: params.getParam(
  146. 'plaats',
  147. ParamType.String,
  148. ),
  149. services: params.getParam(
  150. 'services',
  151. ParamType.String,
  152. ),
  153. ),
  154. ),
  155. FFRoute(
  156. name: EventWidget.routeName,
  157. path: EventWidget.routePath,
  158. builder: (context, params) => EventWidget(
  159. nid: params.getParam(
  160. 'nid',
  161. ParamType.String,
  162. ),
  163. ),
  164. ),
  165. FFRoute(
  166. name: EventCurrentWidget.routeName,
  167. path: EventCurrentWidget.routePath,
  168. builder: (context, params) => EventCurrentWidget(
  169. nid: params.getParam(
  170. 'nid',
  171. ParamType.String,
  172. ),
  173. horecaid: params.getParam(
  174. 'horecaid',
  175. ParamType.String,
  176. ),
  177. ),
  178. ),
  179. FFRoute(
  180. name: HorecagelegenhedenOverzichtProvinciePageWidget.routeName,
  181. path: HorecagelegenhedenOverzichtProvinciePageWidget.routePath,
  182. builder: (context, params) =>
  183. HorecagelegenhedenOverzichtProvinciePageWidget(
  184. plaats: params.getParam(
  185. 'plaats',
  186. ParamType.String,
  187. ),
  188. ),
  189. ),
  190. FFRoute(
  191. name: WachtwoordVergetenWidget.routeName,
  192. path: WachtwoordVergetenWidget.routePath,
  193. builder: (context, params) => WachtwoordVergetenWidget(),
  194. ),
  195. FFRoute(
  196. name: FavorietenWidget.routeName,
  197. path: FavorietenWidget.routePath,
  198. builder: (context, params) => FavorietenWidget(),
  199. ),
  200. FFRoute(
  201. name: KanwegTestUploadWidget.routeName,
  202. path: KanwegTestUploadWidget.routePath,
  203. builder: (context, params) => KanwegTestUploadWidget(),
  204. ),
  205. FFRoute(
  206. name: MijnProfielWidget.routeName,
  207. path: MijnProfielWidget.routePath,
  208. builder: (context, params) => MijnProfielWidget(),
  209. ),
  210. FFRoute(
  211. name: StadsactiviteitAanmakenWidget.routeName,
  212. path: StadsactiviteitAanmakenWidget.routePath,
  213. builder: (context, params) => StadsactiviteitAanmakenWidget(),
  214. ),
  215. FFRoute(
  216. name: UitgaansevenementAanmakenWidget.routeName,
  217. path: UitgaansevenementAanmakenWidget.routePath,
  218. builder: (context, params) => UitgaansevenementAanmakenWidget(
  219. horecagelegenheidNid: params.getParam(
  220. 'horecagelegenheidNid',
  221. ParamType.String,
  222. ),
  223. ),
  224. ),
  225. FFRoute(
  226. name: HorecagelegenhedenOverzichtCurrentWidget.routeName,
  227. path: HorecagelegenhedenOverzichtCurrentWidget.routePath,
  228. builder: (context, params) =>
  229. HorecagelegenhedenOverzichtCurrentWidget(
  230. plaats: params.getParam(
  231. 'plaats',
  232. ParamType.String,
  233. ),
  234. ),
  235. ),
  236. FFRoute(
  237. name: ThuisBezorgenWidget.routeName,
  238. path: ThuisBezorgenWidget.routePath,
  239. builder: (context, params) => ThuisBezorgenWidget(
  240. plaats: params.getParam(
  241. 'plaats',
  242. ParamType.String,
  243. ),
  244. ),
  245. ),
  246. FFRoute(
  247. name: FavorietenCopyWidget.routeName,
  248. path: FavorietenCopyWidget.routePath,
  249. builder: (context, params) => FavorietenCopyWidget(),
  250. ),
  251. FFRoute(
  252. name: FavorietenCopy2Widget.routeName,
  253. path: FavorietenCopy2Widget.routePath,
  254. builder: (context, params) => FavorietenCopy2Widget(),
  255. )
  256. ].map((r) => r.toRoute(appStateNotifier)).toList(),
  257. observers: ffNavigatorObservers,
  258. );
  259. extension NavParamExtensions on Map<String, String?> {
  260. Map<String, String> get withoutNulls => Map.fromEntries(
  261. entries
  262. .where((e) => e.value != null)
  263. .map((e) => MapEntry(e.key, e.value!)),
  264. );
  265. }
  266. extension NavigationExtensions on BuildContext {
  267. void goNamedAuth(
  268. String name,
  269. bool mounted, {
  270. Map<String, String> pathParameters = const <String, String>{},
  271. Map<String, String> queryParameters = const <String, String>{},
  272. Object? extra,
  273. bool ignoreRedirect = false,
  274. }) =>
  275. !mounted || GoRouter.of(this).shouldRedirect(ignoreRedirect)
  276. ? null
  277. : goNamed(
  278. name,
  279. pathParameters: pathParameters,
  280. queryParameters: queryParameters,
  281. extra: extra,
  282. );
  283. void pushNamedAuth(
  284. String name,
  285. bool mounted, {
  286. Map<String, String> pathParameters = const <String, String>{},
  287. Map<String, String> queryParameters = const <String, String>{},
  288. Object? extra,
  289. bool ignoreRedirect = false,
  290. }) =>
  291. !mounted || GoRouter.of(this).shouldRedirect(ignoreRedirect)
  292. ? null
  293. : pushNamed(
  294. name,
  295. pathParameters: pathParameters,
  296. queryParameters: queryParameters,
  297. extra: extra,
  298. );
  299. void safePop() {
  300. // If there is only one route on the stack, navigate to the initial
  301. // page instead of popping.
  302. if (canPop()) {
  303. pop();
  304. } else {
  305. go('/');
  306. }
  307. }
  308. }
  309. extension GoRouterExtensions on GoRouter {
  310. AppStateNotifier get appState => AppStateNotifier.instance;
  311. void prepareAuthEvent([bool ignoreRedirect = false]) =>
  312. appState.hasRedirect() && !ignoreRedirect
  313. ? null
  314. : appState.updateNotifyOnAuthChange(false);
  315. bool shouldRedirect(bool ignoreRedirect) =>
  316. !ignoreRedirect && appState.hasRedirect();
  317. void clearRedirectLocation() => appState.clearRedirectLocation();
  318. void setRedirectLocationIfUnset(String location) =>
  319. appState.updateNotifyOnAuthChange(false);
  320. }
  321. extension _GoRouterStateExtensions on GoRouterState {
  322. Map<String, dynamic> get extraMap =>
  323. extra != null ? extra as Map<String, dynamic> : {};
  324. Map<String, dynamic> get allParams => <String, dynamic>{}
  325. ..addAll(pathParameters)
  326. ..addAll(uri.queryParameters)
  327. ..addAll(extraMap);
  328. TransitionInfo get transitionInfo => extraMap.containsKey(kTransitionInfoKey)
  329. ? extraMap[kTransitionInfoKey] as TransitionInfo
  330. : TransitionInfo.appDefault();
  331. }
  332. class FFParameters {
  333. FFParameters(this.state, [this.asyncParams = const {}]);
  334. final GoRouterState state;
  335. final Map<String, Future<dynamic> Function(String)> asyncParams;
  336. Map<String, dynamic> futureParamValues = {};
  337. // Parameters are empty if the params map is empty or if the only parameter
  338. // present is the special extra parameter reserved for the transition info.
  339. bool get isEmpty =>
  340. state.allParams.isEmpty ||
  341. (state.allParams.length == 1 &&
  342. state.extraMap.containsKey(kTransitionInfoKey));
  343. bool isAsyncParam(MapEntry<String, dynamic> param) =>
  344. asyncParams.containsKey(param.key) && param.value is String;
  345. bool get hasFutures => state.allParams.entries.any(isAsyncParam);
  346. Future<bool> completeFutures() => Future.wait(
  347. state.allParams.entries.where(isAsyncParam).map(
  348. (param) async {
  349. final doc = await asyncParams[param.key]!(param.value)
  350. .onError((_, __) => null);
  351. if (doc != null) {
  352. futureParamValues[param.key] = doc;
  353. return true;
  354. }
  355. return false;
  356. },
  357. ),
  358. ).onError((_, __) => [false]).then((v) => v.every((e) => e));
  359. dynamic getParam<T>(
  360. String paramName,
  361. ParamType type, {
  362. bool isList = false,
  363. StructBuilder<T>? structBuilder,
  364. }) {
  365. if (futureParamValues.containsKey(paramName)) {
  366. return futureParamValues[paramName];
  367. }
  368. if (!state.allParams.containsKey(paramName)) {
  369. return null;
  370. }
  371. final param = state.allParams[paramName];
  372. // Got parameter from `extras`, so just directly return it.
  373. if (param is! String) {
  374. return param;
  375. }
  376. // Return serialized value.
  377. return deserializeParam<T>(
  378. param,
  379. type,
  380. isList,
  381. structBuilder: structBuilder,
  382. );
  383. }
  384. }
  385. class FFRoute {
  386. const FFRoute({
  387. required this.name,
  388. required this.path,
  389. required this.builder,
  390. this.requireAuth = false,
  391. this.asyncParams = const {},
  392. this.routes = const [],
  393. });
  394. final String name;
  395. final String path;
  396. final bool requireAuth;
  397. final Map<String, Future<dynamic> Function(String)> asyncParams;
  398. final Widget Function(BuildContext, FFParameters) builder;
  399. final List<GoRoute> routes;
  400. GoRoute toRoute(AppStateNotifier appStateNotifier) => GoRoute(
  401. name: name,
  402. path: path,
  403. redirect: (context, state) {
  404. if (appStateNotifier.shouldRedirect) {
  405. final redirectLocation = appStateNotifier.getRedirectLocation();
  406. appStateNotifier.clearRedirectLocation();
  407. return redirectLocation;
  408. }
  409. if (requireAuth && !appStateNotifier.loggedIn) {
  410. appStateNotifier.setRedirectLocationIfUnset(state.uri.toString());
  411. return '/home';
  412. }
  413. return null;
  414. },
  415. pageBuilder: (context, state) {
  416. fixStatusBarOniOS16AndBelow(context);
  417. final ffParams = FFParameters(state, asyncParams);
  418. final page = ffParams.hasFutures
  419. ? FutureBuilder(
  420. future: ffParams.completeFutures(),
  421. builder: (context, _) => builder(context, ffParams),
  422. )
  423. : builder(context, ffParams);
  424. final child = appStateNotifier.loading
  425. ? Container(
  426. color: Colors.transparent,
  427. child: Image.asset(
  428. 'assets/images/uitgaanskrant.com-applogo_groter_app.png',
  429. fit: BoxFit.cover,
  430. ),
  431. )
  432. : page;
  433. final transitionInfo = state.transitionInfo;
  434. return transitionInfo.hasTransition
  435. ? CustomTransitionPage(
  436. key: state.pageKey,
  437. name: state.name,
  438. child: child,
  439. transitionDuration: transitionInfo.duration,
  440. transitionsBuilder:
  441. (context, animation, secondaryAnimation, child) =>
  442. PageTransition(
  443. type: transitionInfo.transitionType,
  444. duration: transitionInfo.duration,
  445. reverseDuration: transitionInfo.duration,
  446. alignment: transitionInfo.alignment,
  447. child: child,
  448. ).buildTransitions(
  449. context,
  450. animation,
  451. secondaryAnimation,
  452. child,
  453. ),
  454. )
  455. : MaterialPage(
  456. key: state.pageKey, name: state.name, child: child);
  457. },
  458. routes: routes,
  459. );
  460. }
  461. class TransitionInfo {
  462. const TransitionInfo({
  463. required this.hasTransition,
  464. this.transitionType = PageTransitionType.fade,
  465. this.duration = const Duration(milliseconds: 300),
  466. this.alignment,
  467. });
  468. final bool hasTransition;
  469. final PageTransitionType transitionType;
  470. final Duration duration;
  471. final Alignment? alignment;
  472. static TransitionInfo appDefault() => TransitionInfo(hasTransition: false);
  473. }
  474. class RootPageContext {
  475. const RootPageContext(this.isRootPage, [this.errorRoute]);
  476. final bool isRootPage;
  477. final String? errorRoute;
  478. static bool isInactiveRootPage(BuildContext context) {
  479. final rootPageContext = context.read<RootPageContext?>();
  480. final isRootPage = rootPageContext?.isRootPage ?? false;
  481. final location = GoRouterState.of(context).uri.toString();
  482. return isRootPage &&
  483. location != '/' &&
  484. location != rootPageContext?.errorRoute;
  485. }
  486. static Widget wrap(Widget child, {String? errorRoute}) => Provider.value(
  487. value: RootPageContext(true, errorRoute),
  488. child: child,
  489. );
  490. }
  491. extension GoRouterLocationExtension on GoRouter {
  492. String getCurrentLocation() {
  493. final RouteMatch lastMatch = routerDelegate.currentConfiguration.last;
  494. final RouteMatchList matchList = lastMatch is ImperativeRouteMatch
  495. ? lastMatch.matches
  496. : routerDelegate.currentConfiguration;
  497. return matchList.uri.toString();
  498. }
  499. }