【问题标题】:Flutter Switching to Tab Reloads Widgets and runs FutureBuilderFlutter 切换到 Tab 重新加载小部件并运行 FutureBuilder
【发布时间】:2018-12-15 21:22:30
【问题描述】:

问题:

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

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,它是这样构建的:

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() 标签时,会发生以下情况:它会重新加载。

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

【问题讨论】:

    标签: dart flutter


    【解决方案1】:

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

    TabController 切换选项卡时,它会卸载旧的小部件树以节省内存。如果您想更改此行为,您需要将AutomaticKeepAliveClientMixin 混入您的标签小部件的状态。

    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 可能每帧最多调用一次构建方法)。

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

    相反,您希望在 initState 中将未来分配给 State 类的成员,然后将此值传递给 FutureBuilder。这确保了后续重建的未来是相同的。如果您想强制州重新加载交易,您始终可以创建一个重新分配_loadingDeals 成员并调用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, /* ... */);
    }
    

    【讨论】:

    • 事实上,你用一块石头解决了我的两个问题。非常感谢。真的很重要,解释很有意义,就像解决方案一样。现在工作。
    • 没有initState有什么解决办法吗?因为我正在使用带有 bloc 模式的 streamBuilder。所有数据获取逻辑都在我的 bloc 类中。
    • 勾选此项以避免initState,使用AsyncMemoizer::runOnce() medium.com/saugo360/…
    • 我尝试在我的应用程序中实现这一点,拥有bottomNavigationBar,但每当我更改页面时,它仍然会触发 API。
    • 他们至少可以提供一种方法来真正防止标签被重建。此解决方案仍会重新构建小部件,但只运行未来一次。
    猜你喜欢
    • 2023-04-01
    • 2021-11-29
    • 1970-01-01
    • 1970-01-01
    • 2022-01-02
    • 2019-08-24
    • 2022-09-30
    • 1970-01-01
    • 2020-10-20
    相关资源
    最近更新 更多