textfield reload issue and other things reload problem.===================================

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!

《textfield reload issue and other things reload problem.===================================》  
AbdulRahman AlHamali
Follow 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:

 
《textfield reload issue and other things reload problem.===================================》 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:

 
《textfield reload issue and other things reload problem.===================================》

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!

 

 

    原文作者:CrossPython
    原文地址: https://www.cnblogs.com/pythonClub/p/10719100.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞