【问题标题】:Flutter's FutureBuilder rebuilding multiple timesFlutter 的 FutureBuilder 多次重建
【发布时间】:2020-04-18 00:20:35
【问题描述】:

我对 Flutter 比较陌生,目前正在努力使用 FutureBuilders。 我已经阅读了 Remi 在 thread 上的回答,它基本上说明了 Flutter 的理念,即小部件构建应该是幂等的。我在理论上得到了原理,但是实际上它是如何工作的呢?

考虑以下sn-p:

return Scaffold(
  body: SafeArea(
    child: Stack(
      children: [
        Consumer<DataPresenter>(
            builder: (context, presenter, _) {
              return DefaultFutureBuilder<List<Data>>(
                    future: presenter.data(),
                    builder: (context, data) {
                      // bla bla 
                    }
                );
            }
        ),
        // More widgets
      ],
    ),
  ),
);

}

这是我的DefaultFutureBuilder

class DefaultFutureBuilder<T> extends StatelessWidget {
  final Future<T> future;
  final Widget Function(BuildContext, T data) builder;
  final Widget Function(BuildContext) errorBuilder;

  DefaultFutureBuilder({
    @required this.future,
    @required this.builder,
    this.errorBuilder,
  });

  @override
  Widget build(BuildContext context) {
    var errBuilder = errorBuilder ?? _buildDefaultErrorScreen;

    return FutureBuilder<T>(
      future: future,
      builder: (context, snapshot) {
        var indicator = Center(child: CircularProgressIndicator());

        if (snapshot.connectionState == ConnectionState.done) {
          if (snapshot.hasData)
            return builder(context, snapshot.data);
          else if (snapshot.hasError)
            return errBuilder(context);
        }

        return indicator;
      },
    );
  }

  Widget _buildDefaultErrorScreen(BuildContext context) {
    // default
  }
}


现在我知道对 presenter.data() 的调用(获取一些异步数据)不符合犹太教规。但是我确实想在演示者通知消费者时重新获取数据,同时我不想在 Flutter 由于框架恶作剧而重建我的小部件树时获取它。

我最初的想法是构建演示者,以便它仅在当前没有任何数据时获取数据。然后,我可以通过其他方法将数据设置为 null,并通知侦听器重建受影响的小部件子树。
像这样:

class DataPresenter extends ChangeNotifier {
  // fields, services, bla bla
  List<Data> _data; 

  Future<List<Data>> data() async {
    if (_data == null) {
      var response = await _service.fetchMyStuff(params: params);
      _data = response.data;
    }

    return _data;
  }
}

这个解决方案的问题是,当我第一次获取数据时发生的任何重建都会导致另一个请求。

如果有人能指出我没有得到框架/架构的哪一部分,我将不胜感激。

【问题讨论】:

  • 您的DataPresenter 中似乎有一些拼写错误,对吗?您正在检查_datas 是否为空,否则您将获取,然后将_pets 设置为获取的结果(而不是_datas)。然后你返回_data,而不是_pets而不是_datas
  • 如果你想“重新获取”最新的数据快照,为什么不使用StreamBuilder呢?
  • @matehat,你是对的。我修正了错别字
  • 简而言之:如果您想要一个镜头通知,您可以使用 FutureBuilder,它具有“等待”和“完成”(带有最终数据)状态 - 如果您想要多个通知,您需要一个StreamBuilder 使用最新数据产生“等待”和多个“活动”状态
  • 是的,确切地说,请参阅Creating streams in Dart 了解有关 dart 流的更多信息,您也可以使用 rx-dart 包,为流添加许多有用的东西

标签: flutter future flutter-provider


【解决方案1】:

我认为这可能会解决您的问题我有这个问题太烦人您所做的任何事情以及您的应用程序重建。所以他们解决问题的方法是记住我的未来。现在,这可能对您有用,也可能不适用。如果它将Consumer 重建视为一个新的未来,那么你会很好,因为FutureBuilder 将在Consumer 执行时重建,如果我理解正确的话,这就是你想要的。这就是你要做的。

//Initialize at top of class
final AsyncMemoizer _memoizer = AsyncMemoizer(); 


 Future<void> _someFuture() {
    return this._memoizer.runOnce(() async {
     //Do your async work
    });
  }

然后_someFuture() 将是您传递给FutureBuilder 的内容。这是一篇关于这个问题的好文章。

Flutter: My FutureBuilder Keeps Firing!

【讨论】:

  • 如果我们想在 //Do your async work 中返回一个值怎么办?
【解决方案2】:

如果你想重建一个提供者,那么你可以使用

context.refresh(providerThatYouWantToRebuild);

此方法对于“拉动刷新”或“错误重试”等功能非常有用,可随时重新启动特定的提供程序。

文档位于: https://riverpod.dev/docs/concepts/combining_providers#faq https://pub.dev/documentation/riverpod/latest/all/ProviderContainer/refresh.html https://pub.dev/documentation/flutter_riverpod/latest/all/BuildContextX/refresh.html

【讨论】:

    【解决方案3】:

    我会建议一个没有futureBuilder的解决方案

    您在第一次构建无状态小部件后获取数据

    WidgetsBinding.instance.addPostFrameCallback((_) => Provider.of<DataPresenter>(context, listen: false).fetchdata());
    

    您的 _data 列表与值 nulllenght==0 不同

    class DataPresenter extends ChangeNotifier {
      // fields, services, bla bla
      List<Data> _data; 
      // getter for the Consumer
      List<Data> get data => _data;
    
    
      Future<List<Data>> fetchdata() async {
    
         _data = await _service.fetchMyStuff(params: params);
         if (_data == null) 
           _data = new List(); //difference between null and length==0
    
         //rebuild Consumer
         notifyListeners();
    
      }
    }
    

    没有 FutureBilder 的页面

    class ProvPage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
    
        // calls after the widget ist build
        WidgetsBinding.instance.addPostFrameCallback((_) => Provider.of<TodoListModel>(context, listen: false).refreshTasks());
    
        return MultiProvider(
            providers: [
                  ChangeNotifierProvider(create: (context) => DataPresenter()),
            ],
            child: Scaffold(
              body: SafeArea(
                child: Stack(
                  children: [
                    Consumer<DataPresenter>(
                        builder: (context, presenter, _) {
    
                           //get current Values vom the DataPresenter._data
                           List<Data> currData = presenter.data;
    
                           // firstime fetch ??
                           if(currData == null)
                             return CircularProgressIndicator();
    
                           // DataPresenter.getData is finished
                           return new RefreshIndicator(
                               onRefresh: () => presenter.fetchdata(),
                               child: new Text("length " + currData.length.toString())
                           );
    
                        },
                    ),
                    // More widgets
                  ],
                ),
              ),
            ),
        );
    }
    }
    

    【讨论】:

    • 但是 WidgetBinding 不是在每次重建后添加回调吗?所以每次重建都会调用我的http服务吗?这似乎不对...
    • PostFrameCallback() 每次在整个小部件构建时都会调用,但只有在 notifyListeners() 到来时才会重建消费者
    • 是的,但上面的任何小部件也可以重建。
    • 你也可以在 initstate 中使用一个有状态的小部件来做到这一点
    猜你喜欢
    • 1970-01-01
    • 2021-06-06
    • 2021-12-26
    • 2020-08-23
    • 1970-01-01
    • 2022-11-24
    • 2022-07-23
    • 2021-02-06
    相关资源
    最近更新 更多