nav.dart 19 KB

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