RouterDelegate 不会根据侦听器 Fluter 的更改重建

Posted

技术标签:

【中文标题】RouterDelegate 不会根据侦听器 Fluter 的更改重建【英文标题】:RouterDelegate won't rebuild upon changes from listener Fluter 【发布时间】:2022-01-04 00:52:03 【问题描述】:

我正在尝试按照https://github.com/carloshwa/flutter-example/tree/navigator_2 中的示例切换到 Navigator 2.0,并使其适应我的代码。

在示例中,我有一个class AppState extends ChangeNotifierRouterDelegate 正在收听

AppRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>() 
    appState.addListener(notifyListeners);
    print('appState.addListener(notifyListeners) called');
  

构建正确的页面

pages: [MaterialPage(child: WebPageMainLayout(appState: appState,))],

其中WebPageMainLayout 添加了一个ConstrainedBox 并以WebsitePageDisplay 作为其子级。

WebsitePageDisplay 有一个Column

第一个Column 的小部件是NavigationBar,从中设置按钮AppStateselectedPage

void _handlePageTapped(String selected) 
    print('selected tapped is $selected');
    appState.selectedPage = selected;
  

...

NavigationBarButton(
                title: AppLocalizations.instance.text('For retailers'),
                navigationPath: RetailersLandingRoute,
                onPressed: () ,
                onTapped: _handlePageTapped,
              ),

所以一旦设置它就会通知他的听众(RouterDelegate)。

第二个Column 的小部件是从AppStateselectedWidget getter 返回的实际页面。

return Column(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        NavigationBar(),
        Expanded(
          child: widget.appState.selectedWidget,
        )
      ],
    );

问题是,当按下按钮时,第二个小部件(来自AppState 的吸气剂)没有被交换,面团我从_handlePageTapped 得到正确的打印(例如,上面代码中的`/retailers)

我不确定是不是那个面团的 RouterDelegate,它会收到 AppState 更改的通知,不会用新页面重建 Navigator,或者我只是在执行错误的逻辑,因为我我是 ChangeNotifier 的新手,Navigator 2.0 似乎有点过于复杂。

当页面首次加载时,我会看到我设置的一组打印件,这些打印件来自 parseRouteInformationcurrentConfigurationrestoreRouteInformationsetNewRoutePath' first with no value ( as I don't have a /route ) but then with the correct/cyclists` 值。

AppRouteInformationParser.parseRouteInformation called for /
AppRouteInformationParser uri is /
AppRouteInformationParser.urlSegment switch: /
RouterDelegate.currentConfiguration appState.selectedPage is 
AppRouteInformationParser.restoreRouteInformation called for configuration 
RouterDelegate.setNewRoutePath configuration is Configuration unknown: false, selectedPage: /cyclists
AppState setting selectedPage to /cyclists
RouterDelegate.currentConfiguration appState.selectedPage is /cyclists
AppRouteInformationParser.restoreRouteInformation called for configuration /cyclists
restoreRouteInformation RouteInformation.location: /cyclists
RouterDelegate.currentConfiguration appState.selectedPage is /cyclists
AppRouteInformationParser.restoreRouteInformation called for configuration /cyclists
restoreRouteInformation RouteInformation.location: /cyclists
RouterDelegate.currentConfiguration appState.selectedPage is /cyclists
AppRouteInformationParser.restoreRouteInformation called for configuration /cyclists
restoreRouteInformation RouteInformation.location: /cyclists

但是当我按下NavigationBar 的按钮时,我只得到_handlePageTapped 打印。 在 chrome 中更改 Url 也不起作用.. 你能发现我做错了什么吗?一如既往,非常感谢您的帮助。

应用状态

class AppState extends ChangeNotifier 
  String _selectedPage;
  AppState() : _selectedPage = '';

  String get selectedPage => _selectedPage;

  // works
  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();
    
  

  set selectedPage(String page) 
    print('AppState setting selectedPage to $page');
    _selectedPage = page;
    notifyListeners();
  

路由器代理

class AppRouterDelegate extends RouterDelegate<Configuration>
    with ChangeNotifier, PopNavigatorRouterDelegateMixin<Configuration> 
  final GlobalKey<NavigatorState> navigatorKey;
  AppState appState = AppState();
  AppRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>() 
    appState.addListener(notifyListeners);
    print('appState.addListener(notifyListeners) called');
  


  @override
  Configuration get currentConfiguration 

    print(
        'RouterDelegate.currentConfiguration appState.selectedPage is $appState.selectedPage');
    switch (appState.selectedPage) 
      case CyclistsLandingRoute:
        return Configuration.cyclists(appState.selectedPage);
        break;
      case RetailersLandingRoute:
        return Configuration.retailers(appState.selectedPage);
        break;
      case MapRoute:
        return Configuration.map(appState.selectedPage);
        break;
      case AboutRoute:
        return Configuration.about(appState.selectedPage);
      case TermsOfServiceRoute:
        return Configuration.termsOfService(appState.selectedPage);
      case PrivacyPolicyRoute:
        return Configuration.privacyPolicy(appState.selectedPage);
        break;
      // case PrivacySettingsRoute:
      //   return Configuration.privacySettings();
      //   break;
      case CommunityGuidelinesRoute:
        return Configuration.communityGuidelines(appState.selectedPage);
        break;
      case LegalNoticeRoute:
        return Configuration.legalNotice(appState.selectedPage);
        break;
      default:
        return Configuration.cyclists(appState.selectedPage);
    
  

  @override
  Widget build(BuildContext context) 
    return Navigator(
      key: navigatorKey,
      pages: [
        MaterialPage(
            child: WebPageMainLayout(
          appState: appState,
        ))
      ],
      onPopPage: (route, result) 
        if (!route.didPop(result)) return false;
        if (appState.selectedPage != null) 
          appState.selectedPage = null;
          notifyListeners();
        

        // show404 = false;
        notifyListeners();
        return true;
      ,
    );
  

  @override
  Future<void> setNewRoutePath(Configuration configuration) async 
    print(
        'RouterDelegate.setNewRoutePath configuration is $configuration.toString()');

    if (configuration.isUnknown) 
      // show404 = true;
      appState.selectedPage = null;
      notifyListeners();
      return;
     else if (configuration.isCyclist) 
      appState.selectedPage = configuration.selectedPage;
      notifyListeners();
      return;
     else if (configuration.isRetailer) 
      appState.selectedPage = configuration.selectedPage;
      notifyListeners();
      return;
     else if (configuration.isMap) 
      appState.selectedPage = configuration.selectedPage;
      notifyListeners();
      return;
     else if (configuration.isAbout) 
      appState.selectedPage = configuration.selectedPage;
      notifyListeners();
      return;
     else if (configuration.isTermsOfService) 
      appState.selectedPage = configuration.selectedPage;
      notifyListeners();
      return;
     else if (configuration.isPrivacyPolicy) 
      appState.selectedPage = configuration.selectedPage;
      notifyListeners();
      return;
     else if (configuration.isPrivacySettings) 
      appState.selectedPage = configuration.selectedPage;
      notifyListeners();
      return;
     else if (configuration.isCommunityGuidelines) 
      appState.selectedPage = configuration.selectedPage;
      notifyListeners();
      return;
     else if (configuration.isLegalNotice) 
      appState.selectedPage = configuration.selectedPage;
      notifyListeners();
      return;
    
    return;
  

配置

class Configuration 
  final bool unknown;
  final String selectedPage;

  Configuration.cyclists(this.selectedPage) : unknown = false;
  Configuration.retailers(this.selectedPage) : unknown = false;
  Configuration.map(this.selectedPage) : unknown = false;
  Configuration.about(this.selectedPage) : unknown = false;
  Configuration.termsOfService(this.selectedPage) : unknown = false;
  Configuration.privacyPolicy(this.selectedPage) : unknown = false;
  Configuration.privacySettings(this.selectedPage) : unknown = false;
  Configuration.communityGuidelines(this.selectedPage) : unknown = false;
  Configuration.legalNotice(this.selectedPage) : unknown = false;

  // bool get isHome => unknown == false;
  bool get isCyclist =>
      selectedPage == CyclistsLandingRoute; //unknown == false;
  bool get isRetailer =>
      selectedPage == RetailersLandingRoute; //unknown == false;
  bool get isMap => selectedPage == MapRoute; //unknown == true;
  bool get isAbout => selectedPage == AboutRoute; //unknown == false;
  bool get isTermsOfService =>
      selectedPage == TermsOfServiceRoute; //unknown == false;
  bool get isPrivacyPolicy =>
      selectedPage == PrivacyPolicyRoute; //unknown == false;
  bool get isPrivacySettings =>
      selectedPage == PrivacySettingsRoute; //unknown == false;
  bool get isCommunityGuidelines =>
      selectedPage == CommunityGuidelinesRoute; //unknown == false;
  bool get isLegalNotice =>
      selectedPage == LegalNoticeRoute; //unknown == false;
  bool get isUnknown => unknown == true;

  @override
  String toString() =>
      'Configuration unknown: $unknown, selectedPage: $selectedPage';

解析器

class AppRouteInformationParser extends RouteInformationParser<Configuration> 
  @override
  Future<Configuration> parseRouteInformation(
      RouteInformation routeInformation) async 
    print(
        'AppRouteInformationParser.parseRouteInformation called for $routeInformation.location');

    final Uri uri = Uri.parse(routeInformation.location);
    print('AppRouteInformationParser uri is $uri');

    switch (routeInformation.location) 
      case '/':
        print('AppRouteInformationParser.urlSegment switch: /');
        return Configuration.cyclists(CyclistsLandingRoute);
        break;
      case CyclistsLandingRoute:
        print(
            'AppRouteInformationParser.routeInformation.location switch case: cyclists');
        return Configuration.cyclists(CyclistsLandingRoute);
        break;
      case RetailersLandingRoute:
        print(
            'AppRouteInformationParser.routeInformation.location switch case: retailers');
        return Configuration.retailers(RetailersLandingRoute);
        break;
      case MapRoute:
        print(
            'AppRouteInformationParser.routeInformation.location switch case: map');
        return Configuration.map(MapRoute);
        break;
      case AboutRoute:
        print(
            'AppRouteInformationParser.routeInformation.location switch case: about');
        return Configuration.about(AboutRoute);
        break;
      case TermsOfServiceRoute:
        print(
            'AppRouteInformationParser.routeInformation.location switch case: terms-of-service');
        return Configuration.termsOfService(TermsOfServiceRoute);
        break;
      case PrivacyPolicyRoute:
        print(
            'AppRouteInformationParser.routeInformation.location switch case: privacy-policy');
        return Configuration.privacyPolicy(PrivacyPolicyRoute);
        break;
      case PrivacySettingsRoute:
        print(
            'AppRouteInformationParser.routeInformation.location switch case: privacy-settings');
        return Configuration.privacySettings(PrivacySettingsRoute);
        break;
      case CommunityGuidelinesRoute:
        print(
            'AppRouteInformationParser.routeInformation.location switch case: community-guidelines');
        return Configuration.communityGuidelines(CommunityGuidelinesRoute);
        break;
      case LegalNoticeRoute:
        print(
            'AppRouteInformationParser.routeInformation.location switch case: legal-notice');
        return Configuration.legalNotice(LegalNoticeRoute);
        break;

      default:
        print('AppRouteInformationParser.routeInformation.location: home');
        return Configuration.cyclists(CyclistsLandingRoute);
      // return Configuration.home();
    
  

  @override
  RouteInformation restoreRouteInformation(Configuration configuration) 

    print(
        'AppRouteInformationParser.restoreRouteInformation called for configuration $configuration.selectedPage');
    RouteInformation information = RouteInformation(location: '/cyclists');

    // if (configuration.isHome) 
    //   information = RouteInformation(location: '/');
    //  else
    if (configuration.isCyclist) 
      print('restoreRouteInformation RouteInformation.location: /cyclists');
      information = RouteInformation(location: '/cyclists');
     else if (configuration.isRetailer) 
      print('restoreRouteInformation RouteInformation.location: /retailers');
      information = RouteInformation(location: '/retailers');
     else if (configuration.isMap) 
      print('restoreRouteInformation RouteInformation.location: /map');
      information = RouteInformation(location: '/map');
     else if (configuration.isAbout) 
      print('restoreRouteInformation RouteInformation.location: /about');
      information = RouteInformation(location: '/about');
     else if (configuration.isTermsOfService) 
      print(
          'restoreRouteInformation RouteInformation.location: /terms-of-service');
      information = RouteInformation(location: '/terms-of-service');
     else if (configuration.isPrivacyPolicy) 
      print(
          'restoreRouteInformation RouteInformation.location: /privacy-policy');
      information = RouteInformation(location: '/privacy-policy');
     else if (configuration.isPrivacySettings) 
      print(
          'restoreRouteInformation RouteInformation.location: /privacy-settings');
      information = RouteInformation(location: '/privacy-settings');
     else if (configuration.isCommunityGuidelines) 
      print(
          'restoreRouteInformation RouteInformation.location: /community-guidelines');
      information = RouteInformation(location: '/community-guidelines');
     else if (configuration.isLegalNotice) 
      print('restoreRouteInformation RouteInformation.location: /legal-notice');
      information = RouteInformation(location: '/legal-notice');
    
    // else if (configuration.isUnknown) 
    // information = RouteInformation(location: '/unknown');
    // 

    return information;
  

【问题讨论】:

【参考方案1】:

终于知道我错在哪里了。。 我在一些小部件(NavigationBar)中实例化了一个新的AppState,而不是从RouterDelegate传递那个。 更正后,它按预期工作。 我想专注于新的 Navigator 2.0 系统让我的大脑变得新手哈哈。

【讨论】:

以上是关于RouterDelegate 不会根据侦听器 Fluter 的更改重建的主要内容,如果未能解决你的问题,请参考以下文章

<p:ajax> 监听器永远不会被调用

如何在 FLTK 库中使用 Fl::awake

FL_WHEN_RELEASE 时的 Fl_Tree 回调

鼠标侦听器不会引发错误但不会触发(java)

FLTK:Fl_Value_Input 子类不接收 FL_KEYDOWN 事件(仅 FL_KEYUP)

华硕fl5900 win7驱动