nav.dart 20 KB

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