nav.dart 21 KB

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