后台导航器重建

Offstage Navigators rebuild

我已经为 bottomNavigationBar 实现了多个 Offstage Navigators,可以查看更多详细信息 here。问题是,当使用这种方法时,每次我们 select 一个底部导航项时,FutureBuilder 运行 future 方法并重建整个小部件,每个 Offstage 小部件及其所有 children也重建了。

对于每个 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

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),
        ],
      ),
    );
  }
        

我已遵循此 与提供商一起使用期货的建议,但还缺少其他内容

与其在构建方法中创建未来,正如您所注意到的,可能会调用多次,不如在仅调用一次的地方创建它们。比如一个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<Data>(context) 的上下文会重建。使用 provider 包提供的各种其他小部件可以最方便地完成此操作,例如 ConsumerSelector.

因此删除 Provider<TabProvder>.of(context) 调用并使用 Consumers 和 Selectors。例如,仅在切换选项卡时重建标题:

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

Selector 仅在其 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,
        ),
      ),
    );
}

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