FutureBuilder 的多次触发问题解决

本文最后更新于 2021年4月4日 晚上

在日常使用 FutureBuilder 的时候, 你可能已经发现它会在 Widget 重建触发的时候再去调用 Future 代码, 从而导致重复进行网络请求. 这个问题虽说不影响功能, 但也会对软件质量造成一定影响. 这篇文章就来看看如何处理多次请求的问题.

FutureBuilder 多次触发的解决方案

取自这篇文章.

要解决多次调用 Future 的问题, 就需要从 rebuild 过程入手, 看如何避免在不必要的时候 rebuild, 又或者是在 rebuild 之前处理一下, 让 FutureBuilder 不会再次触发 Future.

State 对象长存, 只有和它关联的 Widget 才会更换, 要注意 State 对象有若干生命期事件需要熟悉:

StatefulWidget 对应的 State 生命期事件有许多, 这里一个一个看:

  • initState: 会在 state 对象首次创建时候调用, 且只会调用一次.

  • build: 在每次重建 Widget 时都会调用.

  • didUpdateWidget: State 关联的 Widget 每次变化后都会调用. 当通过新的输入重建 Widget 后, 老的 Widget 就会被 dispose, 新的 Widget 会被创建出来并被赋值给 State. 而 didUpdateWidget 就是在 Widget 重建之前被调用, 在它里面就可以写我们想要在重建前执行的代码了.

didUpdateWidget 问题

rebuild 时, 会对比前后两个 Future, 如果不同, 则执行新的 Future. 而实际上每次生成的 Future 肯定是不同的, 这个时候就要想一个办法来把之前的 Future 结果缓存下来.

解决方案

可以使用 dart 提供的 AsyncMemoizer 类, 它用于缓存一个函数的返回值. 即给它一个异步函数(比如网络请求的函数), 它会将这个函数在第一次执行的时候进行调用, 然后将结果缓存下来, 之后的请求则每次都返回之前已经计算好的 future.

它的实际用法也非常简单:

1
2
3
4
5
6
7
8
final AsyncMemoizer _memoizer = AsyncMemoizer();

_fetchData() {
return this._memoizer.runOnce(() async {
await Future.delayed(Duration(seconds: 2));
return 'REMOTE DATA';
});
}

需要注意的是, 这个类对象在 StatelessWidget 类中是没有用处的, 因为每次都会重建 StatelessWidget 类, 这样缓存对象也会被释放的, 故使用是没有任何效果的.


FutureBuilder 的多次触发问题解决
https://blog.rayy.top/2019/02/01/2019-46-FutureBuilder-issue/
作者
貘鸣
发布于
2019年2月1日
许可协议