https://github.com/flutter/flutter/issues/18828

https://blog.csdn.net/u011272795/article/details/83010974  <<<<<<<===============

https://medium.com/saugo360/flutter-my-futurebuilder-keeps-firing-6e774830bc2

Flutter: My FutureBuilder Keeps Firing!

 
Aug 19, 2018

If you’ve ever done any serious Flutter development, then you have definitely faced the problem in the title above. But don’t worry, You’re not alone!

What is the problem?

The FutureBuilder widget that Flutter provides us to create widgets based on the state of some future, keeps re-firing that future every time a rebuild happens! The problem is best manifested with this simple example:

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen()
);
}
} class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
} class _HomeScreenState extends State<HomeScreen> { bool _switchValue; @override
void initState() {
super.initState();
this._switchValue = false;
} @override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Switch(
value: this._switchValue,
onChanged: (newValue) {
setState(() {
this._switchValue = newValue;
});
},
),
FutureBuilder(
future: this._fetchData(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator()
);
default:
return Center(
child: Text(snapshot.data)
);
}
}
),
],
),
);
} _fetchData() async {
await Future.delayed(Duration(seconds: 2));
return 'REMOTE DATA';
}
}
  • Our app displays aHomeScreen on which we have one Switch widget and one FutureBuilder that loads remote data and displays it on the screen.
  • The FutureBuilder subscribes to the future returned from the function _fetchData. This function simulates a server that returns its result after two seconds.
  • When the user taps the switch, we update the member _switchValue, which is then fed into the switch itself, to change its actual value.

If you run this app, the following happens:

 

Flipping the switch affects the state of the FutureBuilder

Even though the switch and the FutureBuilder are not related in any form, every time we change the switch value (by calling setState), the FutureBuilder goes through its whole life-cycle again! It re-fetches the future, causing unneeded traffic, and shows the loading again, causing bad user experience.

This problem manifests itself in a variety of ways. In some cases it’s not even as obvious as the example above. For example:

  • Network traffic being generated from pages that are not currently on screen
  • Hot reloading not working properly
  • Losing the Navigator state when updating values in some Inherited Widgets
  • etc…

But what is the cause of all of this? And how can we solve it?

The didUpdateWidget problem

Note: In this section I will take a closer look at how FutureBuilder works. If you’re not interested in this, you can skip to the solution.

If we take a closer look at the code of FutureBuilder, we find that it is a StatefulWidget. As we know, StatefulWidgets maintain a long-livedStateobject. This state has a few methods that manage its life-cycle, like initStatebuild, and didUpdateWidgetinitState is called only once when the state object is first created, and build is called every time we need to build the widget to display, but what what about didUpdateWidget? This method is called whenever the widget attached to this State object changes. When the widget is rebuilt with new inputs, the old widget is disposed, and a new widget is created and assigned to the State object, and didUpdateWidget is called to do anything we want to do before the rebuild. In case of FutureBuilder, this method looks like this:

@override
void didUpdateWidget(FutureBuilder<T> oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.future != widget.future) {
if (_activeCallbackIdentity != null) {
_unsubscribe();
_snapshot = _snapshot.inState(ConnectionState.none);
}
_subscribe();
}
}

It’s basically saying: if, when rebuilt, the new widget has a different Future instance than the old one, then repeat everything: unsubscribe, and subscribe again.

Well, that’s the key! If we provide it with the same future, then it will not refire!

But aren’t we providing the same future? we’re calling the same function! well, it’s not the same future instance. Our function is doing exactly the same work, but then returning a new future, different from the old one.

So, what we want to do is to store or cache the output of the function the first time it is called, and then provide this same output whenever the function is called again. This process is known as memoization.

Solution: Memoize the future

Memoization is, in simple terms, caching the return value of a function, and reusing it when that function is called again. Memoization is mostly used in functional languages, where functions are deterministic (they always return the same output for the same inputs), but we can use simple memoization for our problem here, to make sure the FutureBuilder always receives the same future instance.

To do that, we will use Dart’s AsyncMemoizer. This memoizer does exactly what we want! It takes an asynchronous function, calls it the first time it is called, and caches its result. For all subsequent calls to the function, the memoizer returns the same previously calculated future.

Thus, to solve our problem, we start by creating an instance of AsyncMemoizerin our widget:

final AsyncMemoizer _memoizer = AsyncMemoizer();

Note: you shouldn’t instantiate the memoizer inside a StatelessWidget, because Flutter disposes of StatelessWidgets at every rebuild, which basically beats the purpose. You should instantiate it either in a StatefulWidget, or somewhere where it can persist.

Afterwards, we will modify our _fetchData function to use that memoizer:

_fetchData() {
return this._memoizer.runOnce(() async {
await Future.delayed(Duration(seconds: 2));
return 'REMOTE DATA';
});
}
  • We wrapped our function with AsyncMemoizer.runOnce which does exactly what it sounds like; it runs the function only once, and when called again, returns the cached future.

And that’s it! Our FutureBuilder now only fires the first time around:

 

Thus, our FutureBuilder was getting confused, and thinking that a new future was passed to it, causing it to refire every time. To solve this, we used Dart’s AsyncMemoizer to pass the same future instance every time.

I hope this has been helpful! Let me know what you think!

textfield reload issue and other things reload problem.===================================的更多相关文章

  1. eclipse 打开是报错"reload maven project has encountered a problem"

    不需要删除整个 .metadata 如果删除这个代价是重新导入全部项目 D:\eclipse-workspace\.metadata\.plugins\org.eclipse.e4.workbench ...

  2. 在IE浏览器中执行OpenFlashChart的reload方法时无法刷新的解决方法

    由于项目需求,需要在网页上利用图表展示相关数据的统计信息,采用了OpenFlashChart技术.OpenFlashChart是一款开源的以Flash和Javascript为技术基础的免费图表,用它能 ...

  3. javascript 中的location.reload

    location.reload()是什么意思 location.reload() 括号内有一个参数 true/false , 为空和false的效果一样. 如果该方法没有规定参数,或者参数是 fals ...

  4. 把Nginx加入系统服务 service nginx (start | stop | restart | reload)

    vim /etc/init.d/nginx 1 #!/bin/bash  2 # nginx Startup script for the Nginx HTTP Server  3 # it is v ...

  5. 使用Tomcat的Reload提高开发速度(翻译)

    欢迎转载http://www.cnblogs.com/coodream2009,有翻译的不太准确的地方请大家指出,我继续修改完善. 按照Java Servlet规范第四部分推荐的,Tomcat系统的实 ...

  6. layui table数据表格reload where参数保留问题

    layui table数据表格reload where参数保留问题 在使用layui过程中多多少少会遇到些问题 table reload 有个坑:reload时where参数会保留上次的参数,如果用 ...

  7. javascript:location.reload()和location.replace()的区别,及对图片缓存的影响。

    有段时间没有清理IE的临时文件(缓存文件),在我清理的时候,我突然发现一个问题. 我打开的一个网站,图片默认缓存一个月的,但我发现,当我上传图片或删除图片之后,图片重新缓存,也就意味着,在我上传新图片 ...

  8. Python中的reload函数

    Python中的import语句可以导入module文件,但是import语句只是第一次导入的时候会执行module文件中的代码,然后就会把导入的模块文件存入到内存,当再次导入的时候,Python是直 ...

  9. python之reload用法

    一.python2和python3的区别 python2中可以直接使用reload().python3中需要从库中导入,有两种方法: >>> from imp import relo ...

随机推荐

  1. MVC 向页面传值方式总结(2)

    MVC 向页面传值方式总结 总结发现ASP.NET MVC中Controller向View传值的方式共有6种,分别是: ViewBag ViewData TempData 向普通View页面传一个Mo ...

  2. Selenium 3----警告框处理+下拉框选择

    警告框处理 在WebDriver中处理JavaScript所生成的alert.confirm以及prompt十分简单,具体做法是使用 switch_to.alert 方法定位到 alert/confi ...

  3. ELK之使用packetbeat分析网络包流量

    介绍 packbeat是一个开源的实时网络抓包与分析框架,内置了很多常见的协议捕获及解析,如HTTP.MySQL.Redis等.在实际使用中,通常和Elasticsearch以及kibana联合使用, ...

  4. 解析数学表达式 代码解析AST语法树

    2019年2月20日09:18:22 AST语法树自己写代码解析的话就比较麻烦,有现成的库可以解析PHP,就像webpack就是自己解析js的语法代码,编译成各种版本的可用代码 github http ...

  5. yum配置163源

    CentOS7 配置163 yum源 1)下载repo文件 wget http://mirrors.163.com/.help/CentOS7-Base-163.repo 2)备份并替换系统的repo ...

  6. git命令行在windows中报错WARNING: terminal is not fully functional

    今天在windows的PowerShell中执行git log命令的时候报错: WARNING: terminal is not fully functional 网上查找方法,最后 1.打开wind ...

  7. vue 实现子向父传值

    父组件 <template> <div id="app"> <child @onChange='onChildValue'></child ...

  8. 找出sql脚本中需要创建的表空间名称和数据库用户名

    测试的工作中,经常会遇到项目交接或者搭建一个新的测试环境,而创建oracle数据库用户及表空间时,需要提前找出脚本中的 数据库用户名和表空间名,所以自己写了一个python脚本,自动找出sql脚本中的 ...

  9. VisualStudioCode创建的asp.net core控制台程序部署到linux

    1.asp.net core控制台程序 static void Main(string[] args) { ; ) { Console.WriteLine("Hello World!&quo ...

  10. Vue项目在开发环境跨域和生成环境部署跨域问题解决

    一.在dev环境下的跨域问题解决1.项目使用的是axios请求网络,将baseUrl修改为/api (这里是使用webpack提供的代理功能将/api代理成目标接口host) axios.defaul ...