Flutter 切换到 Tab 重新加载小部件并运行 FutureBuilder [英] Flutter Switching to Tab Reloads Widgets and runs FutureBuilder

查看:20
本文介绍了Flutter 切换到 Tab 重新加载小部件并运行 FutureBuilder的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题:

我有 2 个使用默认选项卡控制器的选项卡,如下所示:

I have 2 tabs using Default Tabs Controller, like so:

Widget build(BuildContext context) {
    return DefaultTabController(
      length: 2,
      child: Scaffold(
        drawer: Menu(),
        appBar: AppBar(
          title: Container(
            child: Text('Dashboard'),
          ),
          bottom: TabBar(
            tabs: <Widget>[
              Container(
                padding: EdgeInsets.all(8.0),
                child: Text('Deals'),
              ),
              Container(
                padding: EdgeInsets.all(8.0),
                child: Text('Viewer'),
              ),
            ],
          ),
        ),
        body: TabBarView(
          children: <Widget>[
            DealList(),
            ViewersPage(),
          ],
        ),
      ),
    );
  }
}

DealList() 是一个 StatefulWidget,它是这样构建的:

The DealList() is a StatefulWidget which is built like this:

Widget build(BuildContext context) {
    return FutureBuilder(
      future: this.loadDeals(),
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        print('Has error: ${snapshot.hasError}');
        print('Has data: ${snapshot.hasData}');
        print('Snapshot data: ${snapshot.data}');
        return snapshot.connectionState == ConnectionState.done
            ? RefreshIndicator(
                onRefresh: showSomething,
                child: ListView.builder(
                  physics: const AlwaysScrollableScrollPhysics(),
                  itemCount: snapshot.data['deals'].length,
                  itemBuilder: (context, index) {
                    final Map deal = snapshot.data['deals'][index];
                    print('A Deal: ${deal}');
                    return _getDealItem(deal, context);
                  },
                ),
              )
            : Center(
                child: CircularProgressIndicator(),
              );
      },
    );
  }
}

根据上述内容,当我切换回 DealList() 选项卡时会发生以下情况:它重新加载.

With the above, here's what happens whenever I switch back to the DealList() tab: It reloads.

有没有办法防止 FutureBuilder 在完成一次后重新运行?(计划是让用户使用 RefreshIndicator 重新加载.因此,除非用户明确这样做,否则更改选项卡不应触发任何内容.)

Is there a way to prevent re-run of the FutureBuilder when done once? (the plan is for user to use the RefreshIndicator to reload. So changing tabs should not trigger anything, unless explicitly done so by user.)

推荐答案

这里有两个问题,第一个:

There are two issues here, the first:

TabController 切换选项卡时,它会卸载旧的小部件树以节省内存.如果你想改变这种行为,你需要混合 AutomaticKeepAliveClientMixin 到您的标签小部件的状态.

When the TabController switches tabs, it unloads the old widget tree to save memory. If you want to change this behavior, you need to mixin AutomaticKeepAliveClientMixin to your tab widget's state.

class _DealListState extends State<DealList> with AutomaticKeepAliveClientMixin<DealList> {
  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context); // need to call super method.
    return /* ... */
  }
}

第二个问题是您对 FutureBuilder 的使用 -如果你给一个 FutureBuilder 提供一个新的 Future,它无法判断结果会和上次一样,所以它必须重建.(请记住,Flutter 可能每帧最多调用一次您的构建方法).

The second issue is in your use of the FutureBuilder - If you provide a new Future to a FutureBuilder, it can't tell that the results would be the same as the last time, so it has to rebuild. (Remember that Flutter may call your build method up to once a frame).

return FutureBuilder(
  future: this.loadDeals(), // Creates a new future on every build invocation.
  /* ... */
);

相反,您希望将未来分配给 initState 中 State 类的成员,然后将此值传递给 FutureBuilder.这确保了后续重建的未来是相同的.如果您想强制 State 重新加载交易,您始终可以创建一个方法来重新分配 _loadingDeals 成员并调用 setState.

Instead, you want to assign the future to a member on your State class in initState, and then pass this value to the FutureBuilder. The ensures that the future is the same on subsequent rebuilds. If you want to force the State to reload the deals, you can always create a method which reassigns the _loadingDeals member and calls setState.

Future<...> _loadingDeals;

@override
void initState() {
  _loadingDeals = loadDeals(); // only create the future once.
  super.initState();
}

@override
Widget build(BuildContext context) {
  super.build(context); // because we use the keep alive mixin.
  return new FutureBuilder(future: _loadingDeals, /* ... */);
}

这篇关于Flutter 切换到 Tab 重新加载小部件并运行 FutureBuilder的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆