nav.dart 20 KB

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