nav.dart 20 KB

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