Кнопка «Назад» браузера не запускает информацию о парсере и выдает дубликат глобального ключа, обнаруженного в дереве виджетов.

#flutter-web #flutter-navigation

Вопрос:

Я переключаюсь на Navigator 2.0 и могу изменить страницу с помощью кнопок навигации в своем NavigationBar виджете, но при нажатии кнопки браузера «Назад» или «вперед» страница не обновляется, отображается URL-адрес. Если я нажму его во второй раз, он вернется, но выдаст ошибку. Duplicate GlobalKey detected in widget tree.

 The following assertion was thrown while finalizing the widget tree: Duplicate GlobalKey detected in widget tree.  The following GlobalKey was specified multiple times in the widget tree. This will lead to parts of the widget tree being truncated unexpectedly, because the second time a key is seen, the previous instance is moved to the new location. The key was: - [LabeledGlobalKeylt;NavigatorStategt;#781da] This was determined by noticing that after the widget with the above global key was moved out of its previous parent, that previous parent never updated during this frame, meaning that it either did not update at all or updated before the widget was moved, in either case implying that it still thinks that it should have a child with that global key. The specific parent that did not update after having one or more children forcibly removed due to GlobalKey reparenting is: - Builder A GlobalKey can only be specified on one widget at a time in the widget tree. When the exception was thrown, this was the stack:  dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 251:49 throw_ packages/flutter/src/widgets/framework.dart 2939:15 lt;fngt; packages/flutter/src/widgets/framework.dart 2963:16 finalizeTree packages/flutter/src/widgets/binding.dart 884:7 drawFrame packages/flutter/src/rendering/binding.dart 319:5 [_handlePersistentFrameCallback] packages/flutter/src/scheduler/binding.dart 1143:15 [_invokeFrameCallback] packages/flutter/src/scheduler/binding.dart 1080:9 handleDrawFrame packages/flutter/src/scheduler/binding.dart 996:5 [_handleDrawFrame] lib/_engine/engine/platform_dispatcher.dart 1003:13 invoke lib/_engine/engine/platform_dispatcher.dart 157:5 invokeOnDrawFrame lib/_engine/engine.dart 440:45 lt;fngt;  

все мои виджеты, независимо от того, находятся ли они в состоянии ОС без состояния, имеют const WidgetName({Key key}) : super(key: key) конструктор, и при их создании я никогда не передаю родительский ключ ребенку..

Я попытался удалить Key из конструкторов виджетов, но все равно получаю ошибку..

Я удалил ключ из навигатора, RouterDelegate и повторяющаяся ошибка исчезла, но все равно требуется 2 нажатия кнопки «Назад», чтобы вернуться на предыдущую страницу, и редактирование URL-адреса вручную выдает ошибку:

 Could not navigate to initial route. The requested route name was: "/retailers" There was no corresponding route in the app, and therefore the initial route specified will be ignored and "/" will be used instead.  

В моем переопределенном parseRouteInformation я устанавливаю отпечатки, которые отображаются при запуске приложения, но не при нажатии кнопки браузера «Назад» или «вперед».

Можете ли вы определить, почему RouteInformationParser не выполняется разбор предыдущего маршрута и выдает Duplicate GlobalKey detected in widget tree. ошибку?

Я проверил, и тот, кто вошел RouterDelegate , — единственный, кого я объявил. Есть ли что-нибудь еще, что вам нужно узнать о моем приложении?

это мой RouterDelegate:

 class AppRouterDelegate extends RouterDelegatelt;RoutePathgt;  with ChangeNotifier, PopNavigatorRouterDelegateMixinlt;RoutePathgt; {   final GlobalKeylt;NavigatorStategt; navigatorKey;  AppState appState = AppState();  AppRouterDelegate() : navigatorKey = GlobalKeylt;NavigatorStategt;() {  appState.addListener(notifyListeners);  print('appState.addListener(notifyListeners) called');  }  @override  RoutePath get currentConfiguration {  print(  'RouterDelegate.currentConfiguration appState.selectedPage is ${appState.selectedPage}');   switch (appState.selectedPage) {  case '/':  return HomePath();  case CyclistsLandingRoute:  return CyclistsPath();  case RetailersLandingRoute:  return RetailersPath();  case MapRoute:  return MapPath();  case AboutRoute:  return AboutPath();  case TermsOfServiceRoute:  return TermsOfServicePath();  case PrivacyPolicyRoute:  return PrivacyPolicyPath();  case PrivacySettingsRoute:  return PrivacySettingsPath();  case CommunityGuidelinesRoute:  return CommunityGuidelinesPath();  case LegalNoticeRoute:  return LegalPath();  default:  return HomePath();  }  }   @override  Widget build(BuildContext context) {  print("Delegate build");  return Navigator(  key: navigatorKey,  pages: [  MaterialPage(  child: WebsitePageDisplay(  appState: appState,  ))  ],  onPopPage: (route, result) {  print("onPopPage");  if (!route.didPop(result)) {  return false;  }  if (appState.selectedPage != null) {  appState.selectedPage = null;  }   notifyListeners();  return true;  }  );  }   @override  Futurelt;voidgt; setNewRoutePath(RoutePath path) async {  print('RouterDelegate.setNewRoutePath path is ${path.selectedPath}');  appState.selectedPage = path.selectedPath;  } }  

this is my RouteInformationParser:

 class AppRouteInformationParser extends RouteInformationParserlt;RoutePathgt; {  @override  Futurelt;RoutePathgt; parseRouteInformation(  RouteInformation routeInformation) async {  print(  'AppRouteInformationParser.parseRouteInformation called for ${routeInformation.location}');  final Uri uri = Uri.parse(routeInformation.location);  if (uri.pathSegments.length gt; 0) {  print(  'Uri.segments.first is: ${uri.pathSegments.first}, uri.path is: ${uri.path}');  } else {  print('AppRouteInformationParser uri has no segments and is $uri');  }   switch (routeInformation.location) {  // switch (uri.pathSegments.first) {  case '/':  print('AppRouteInformationParser.urlSegment switch case : /');  // return CyclistsPath();  return HomePath();  case CyclistsLandingRoute:  print(  'AppRouteInformationParser.routeInformation.location switch case: /cyclists');  return CyclistsPath();  case '/retailers':  print(  'AppRouteInformationParser.routeInformation.location switch case: /retailers');  return RetailersPath();  case '/map':  print(  'AppRouteInformationParser.routeInformation.location switch case: /map');  return MapPath();  case AboutRoute:  print(  'AppRouteInformationParser.routeInformation.location switch case: /about');  return AboutPath();  case TermsOfServiceRoute:  print(  'AppRouteInformationParser.routeInformation.location switch case: /terms-of-service');  return TermsOfServicePath();  case PrivacyPolicyRoute:  print(  'AppRouteInformationParser.routeInformation.location switch case: /privacy-policy');  return PrivacyPolicyPath();  case PrivacySettingsRoute:  print(  'AppRouteInformationParser.routeInformation.location switch case: /privacy-settings');  return PrivacySettingsPath();  case CommunityGuidelinesRoute:  print(  'AppRouteInformationParser.routeInformation.location switch case: /community-guidelines');  return CommunityGuidelinesPath();  case LegalNoticeRoute:  print(  'AppRouteInformationParser.routeInformation.location switch case: /legal-notice');  return LegalPath();   default:  print(  '### default AppRouteInformationParser.routeInformation.location switch case ## default: /');  return HomePath();  }   }   @override  RouteInformation restoreRouteInformation(RoutePath path) {   print(  'AppRouteInformationParser.restoreRouteInformation called for path ${path.selectedPath}');   switch (path.selectedPath) {  case '/':  // case CyclistsLandingRoute:  print('restoreRouteInformation RouteInformation.location: /');  return RouteInformation(location: '/');  case '/cyclists':  // case CyclistsLandingRoute:  print('restoreRouteInformation RouteInformation.location: /cyclists');  return RouteInformation(location: '/cyclists');   case '/retailers':  print('restoreRouteInformation RouteInformation.location: /retailers');  return RouteInformation(location: '/retailers');  case '/map':  print('restoreRouteInformation RouteInformation.location: /map');  return RouteInformation(location: '/map');  case '/about':  print('restoreRouteInformation RouteInformation.location: /about');  return RouteInformation(location: '/about');  case '/terms-of-service':  print(  'restoreRouteInformation RouteInformation.location: /terms-of-service');  return RouteInformation(location: '/terms-of-service');  case '/privacy-policy':  print(  'restoreRouteInformation RouteInformation.location: /privacy-policy');  return RouteInformation(location: '/privacy-policy');  case '/privacy-settings':  print(  'restoreRouteInformation RouteInformation.location: /privacy-settings');  return RouteInformation(location: '/privacy-settings');  case '/community-guidelines':  print(  'restoreRouteInformation RouteInformation.location: /community-guidelines');  return RouteInformation(location: '/community-guidelines');  case '/legal-notice':  print(  'restoreRouteInformation RouteInformation.location: /legal-notice');  return RouteInformation(location: '/legal-notice');  default:  print(  'restoreRouteInformation ### Default RouteInformation.location: /cyclists');  return RouteInformation(location: '/cyclists');  }  } }  

and this is AppState:

 class AppState extends ChangeNotifier {  String _selectedPage;  AppState() : _selectedPage = null;   String get selectedPage =gt; _selectedPage;   Widget get selectedWidget {  switch (selectedPage) {  case CyclistsLandingRoute:  return CyclistLanding();  break;  case RetailersLandingRoute:  return RetailerLanding();  break;  case MapRoute:  return CityMap();  break;  case AboutRoute:  return AboutUs();  break;  case TermsOfServiceRoute:  return TermsOfService();  break;  case PrivacyPolicyRoute:  return PrivacyPolicy();  break;  // case PrivacySettingsRoute:  // return PrivacyPolicySettings();  // break;  case CommunityGuidelinesRoute:  return CommunityGuidelines();  break;  case LegalNoticeRoute:  return LegalNotice();  break;  default:  return CyclistLanding();  notifyListeners();  }  notifyListeners();  }   set selectedPage(String page) {  print('AppState setting selectedPage to $page');  _selectedPage = page;  notifyListeners();  } }  

Ответ №1:

Наконец-то я нашел проблему:

В методе сборки main.dart я возвращал a MaterialApp с a MaterialApp.router в качестве дома вместо a MaterialApp.router напрямую и перемещал в него все параметры. Теперь все работает так, как и ожидалось.

не в ту сторону :

 AppRouterDelegate _routerDelegate = AppRouterDelegate();   AppRouteInformationParser _routeInformationParser =  AppRouteInformationParser();   @override  Widget build(BuildContext context) {  return MaterialApp(  title: '',  color: Colors.red,  localizationsDelegates: [  const AppLocalizationsDelegate(),  GlobalMaterialLocalizations.delegate,  GlobalCupertinoLocalizations.delegate,  GlobalWidgetsLocalizations.delegate,  ],  supportedLocales: [  const Locale('en', 'US'),  const Locale('it', 'IT') // const Locale('es', 'ES'),  ],  localeResolutionCallback:  (Locale locale, Iterablelt;Localegt; supportedLocales) {  for (Locale supportedLocale in supportedLocales) {  if (supportedLocale.languageCode == locale.languageCode ||  supportedLocale.countryCode == locale.countryCode) {  print('Web device Locale is $locale');  return supportedLocale;  }  }  return supportedLocales.first;  },  debugShowCheckedModeBanner: false,  home: MaterialApp.router(  routeInformationParser: _routeInformationParser,  routerDelegate: _routerDelegate),  );  }  

правильный путь-это :

 AppRouterDelegate _routerDelegate = AppRouterDelegate();   AppRouteInformationParser _routeInformationParser =  AppRouteInformationParser();   @override  Widget build(BuildContext context) {  return MaterialApp.router(  routeInformationParser: _routeInformationParser,  routerDelegate: _routerDelegate,  debugShowCheckedModeBanner: false,  title: '',  color: Colors.red,  localizationsDelegates: [  const AppLocalizationsDelegate(),  GlobalMaterialLocalizations.delegate,  GlobalCupertinoLocalizations.delegate,  GlobalWidgetsLocalizations.delegate,  ],  supportedLocales: [  const Locale('en', 'US'),  const Locale('it', 'IT') // const Locale('es', 'ES'),  ],  localeResolutionCallback:  (Locale locale, Iterablelt;Localegt; supportedLocales) {  for (Locale supportedLocale in supportedLocales) {  // if (UniversalPlatform.isWeb) {  if (supportedLocale.languageCode == locale.languageCode ||  supportedLocale.countryCode == locale.countryCode) {  print('Web device Locale is $locale');  return supportedLocale;  }  }  return supportedLocales.first;  },  // localeListResolutionCallback: ,  );  }