【问题标题】:Offstage Navigators rebuild后台导航器重建
【发布时间】:2021-01-10 12:02:14
【问题描述】:

我已经为bottomNavigationBar实现了多个OffstageNavigators,更多细节可以看here。问题是在使用这个方法的时候,我们每选择一个底部导航项,FutureBuilder就会运行future方法并重建整个widget,每个Offstagewidget及其所有子widget也会重建。

对于每个 Offstage 小部件,我通过 html 请求加载数据,这意味着每次切换选项卡时,将发出 5 个请求。

这是我的主要Scaffold,其中包含bottomNavigationBar

@override
  Widget build(BuildContext context) {
    TabProvider tabProvider = Provider.of<TabProvider>(context);
    return FutureBuilder(
      future: initProvider(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          return WillPopScope(
            onWillPop: _onWillPop,
            child: Scaffold(
              appBar: AppBar(
                title: Text(tabName[tabProvider.currentTab],
              ),
              body: Stack(children: <Widget>[
                _buildOffstageNavigator(TabItem.feed, tabProvider.currentTab),
                _buildOffstageNavigator(TabItem.explore, tabProvider.currentTab),
                _buildOffstageNavigator(TabItem.guide, tabProvider.currentTab),
                _buildOffstageNavigator(TabItem.map, tabProvider.currentTab),
                _buildOffstageNavigator(TabItem.profile, tabProvider.currentTab),
              ]),
              bottomNavigationBar: BottomNavigation(
                currentTab: tabProvider.currentTab,
                onSelectTab: tabProvider.selectTab,
              ),
            ),
          );
        } else {
          return Text('Loading');
        }
      },
    );
  }

FutureBuilder 将初始化我的提供程序中的值,以便每个选项卡都可以访问缓存的数据。

_buildOffstageNavigator 将返回以下内容

return Offstage(
      offstage: currentTab != tabItem,
      child: TabNavigator(
        navigatorKey: navigatorKeys[tabItem],
        tabItem: tabItem,
      ),
    );

下面是构建在Scaffold 主体内的小部件,因此在上面的Offstage Navigator 内。

@override   
  Widget build(BuildContext context) {
    TabProvider tabProvider = Provider.of<TabProvider>(context);
    States stateData = tabProvider.exploreStateCache;
    return Container(
      child: ListView(
        children: <Widget>[
          Text(stateData.stateName),
          Text(stateData.stateDescription),
        ],
      ),
    );
  }
        

我已按照articles 的建议在提供商处使用期货,但缺少其他内容

【问题讨论】:

    标签: flutter dart asynchronous provider


    【解决方案1】:

    不要在构建方法中创建期货,正如您所注意到的,它可能会被多次调用,而是在只被调用一次的地方创建它们。比如一个StatefulWidget的initState:

    class Foo extends StatefulWidget {
      @override
      _FooState createState() => _FooState();
    }
    
    class _FooState extends State<Foo> {
      Future<MyData> _dataFuture;
    
      @override
      void initState() {
        super.initState();
        _dataFuture = getData();
      }
    
      @override
      Widget build(BuildContext context) => FutureBuilder<MyData>(
            future: _dataFuture,
            builder: (context, snapshot) => ...,
          );
    }
    

    您可以改进的第二件事是在提供者为TabProvider 提供新值时缩小重建的范围。当Data 有新值时,您调用Provider.of&lt;Data&gt;(context) 的上下文将被重建。使用提供程序包提供的各种其他小部件(如 ConsumerSelector)最方便地完成此操作。

    所以删除Provider&lt;TabProvder&gt;.of(context) 调用并使用Consumers 和Selectors。例如,仅在切换选项卡时重建标题:

    AppBar(
      title: Selector<TabProvider, String>(
        selector: (context, tabProvider) => tabName[tabProvider.currentTab],
        builder: (context, title) => Text(title),
      ),
    )
    

    选择器仅在其selector 回调的结果与之前的值不同时重建Text(title) 小部件。 _buildOffstageNavigator 也是如此:

    Widget _buildOffstageNavigator(BuildContext context, TabItem tabItem) {
      return Selector<TabProvider, bool>(
        selector: (context, tabProvider) => tabProvider.currentTab != tabItem,
        builder: (context, isCurrent) => Offstage(
          offstage: isCurrent,
          child: Selector<TabProvider, Key>(
            selector: (context, tabProvider) => tabProvider.navigatorKeys[tabItem],
            builder: (context, tabKey) => TabNavigator(
              navigatorKey: tabKey,
              tabItem: tabItem,
            ),
          ),
        );
    }
    

    (注意:所有代码未经测试且包含拼写错误)

    【讨论】:

    • 由于_dataFuture = getData();会初始化provider使用的变量,所以init状态函数不能使用Provider.of(context)。有没有其他办法解决这个问题?
    • 如果需要在initState中使用context,可以将代码移动到didChangeDependencies,让它懒惰地初始化变量。例如:variableInState ??= Provider.of&lt;Data&gt;(context, listen: false);(仅一次)或variableInState = Provider.of&lt;Data&gt;(context);(数据更改时重新构建小部件)。我不确定这是否有帮助,因为我不明白您的确切意思。
    猜你喜欢
    • 2016-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多