The average programmer knows that things like IO and network requests should be asynchronous.
In Dart, we use the Future for management so we don’t have to worry about threads or deadlocks.
How should widgets be built when Flutter involves a Future?
How should we manage our UI before, during, completed, or failed web requests?
To this end, Flutter launched FutureBuilder.
What is a FutureBuilder
First look at the documentation:
Widget that builds itself based on the latest snapshot of interaction with a Future.
FutureBuilder is a component that builds itself on a Future snapshot.
What the hell is a snapshot? Personal understanding is the current information of this Future.
This information includes: current status, carried data, and so on.
How to use
To see what FutureBuilder is, click the source code:
class FutureBuilder<T> extends StatefulWidget {
const FutureBuilder({
Key key,
this.future,
this.initialData,
@required this.builder,
}) : assert(builder ! =null),
super(key: key);
}
final AsyncWidgetBuilder<T> builder;
Copy the code
Recognize that it is a stateful widget and find the build method for State:
Widget build(BuildContext context) => widget.builder(context, _snapshot);
The Build method returns a Widget builder directly.
Let’s go ahead and open the official website to see how the official Demo is written:
FutureBuilder<String>(
future: _calculation, // a previously-obtained Future<String> or null
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Press button to start.');
case ConnectionState.active:
case ConnectionState.waiting:
return Text('Awaiting result... ');
case ConnectionState.done:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
return Text('Result: ${snapshot.data}');
}
return null; // unreachable},)Copy the code
You can see that FutureBuilder defines a generic type that is used to retrieve data from a snapshot.
FlutureBuilder takes two parameters:
Future: This parameter requires a Future object, similar to network request, IO
Builder: This parameter returns a widget, and we can see that the demo returns different widgets depending on the connection status of the current snapshot.
Let’s take a look at snapshot.connectionState:
ConnectionState | No asynchronous tasks are currently connected to |
---|---|
ConnectionState.none | No asynchronous tasks are currently connected to |
ConnectionState.waiting | Connect to the asynchronous task and wait to interact |
ConnectionState.active | Connect to the asynchronous task and start the interaction |
ConnectionState.done | Asynchronous Task Abort |
Now that we know, we can have an idea.
We can use the state of the current snapshot to return different widgets, such as:
First look at the build code:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('FutureBuilderPage'),
),
body: FutureBuilder(
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.active:
case ConnectionState.waiting:
print('waiting');
return Center(child: CupertinoActivityIndicator());
case ConnectionState.done:
print('done');
if (snapshot.hasError) {
return Center(
child: Text('Network request error')); }return generateListView();
}
return null;
},
future: _future,
),
);
}
Copy the code
The Scaffold body directly returns a FutureBuilder that returns different widgets depending on the state.
One thing to note here is that we know that StatefulWidget maintains a State for a long time and calls the didUpdateWidget method when changes are made, requiring a rebuild. So FutureBuilder’s official documentation reads:
The future must have been obtained earlier, e.g. during State.initState,
State.didUpdateConfig
, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.buildmethod call when constructing the FutureBuilder. If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder’s parent is rebuilt, the asynchronous task will be restarted.A general guideline is to assume that every
build
method could get called every frame, and to treat omitted calls as an optimization.
The future parameter is recommended to be initialized in initState(), not in the build method, which will always be rebuilt.
Why? Look at the source code for didUpdateWidget:
@override
void didUpdateWidget(FutureBuilder<T> oldWidget) {
super.didUpdateWidget(oldWidget);
if(oldWidget.future ! = widget.future) {if(_activeCallbackIdentity ! =null) { _unsubscribe(); _snapshot = _snapshot.inState(ConnectionState.none); } _subscribe(); }}Copy the code
We must not initialize the future parameter in the build method.
So, we initialize in the initState() method:
Future<List<Stories>> _future;
Dio _dio;
int date = 20190523;
@override
void initState() {
super.initState();
_dio = Dio();
_future = getNewsList();
}
// Get daily news from Zhihu
Future<List<Stories>> getNewsList() async {
var response =
await _dio.get('https://news-at.zhihu.com/api/4/news/before/$date');
return ZhiHuNews.fromJson(response.data)._stories;
}
Copy the code
The generateListView method is the basic ListView.Builder () method.
This completes the effect shown above, loading the chrysanthemum when the network request is made and the request is successfully loaded out of the ListView.
summary
As you can see, FutureBuilder is very convenient, and we can encapsulate several controls ourselves, which will be perfect for later use.
If you want to get the source code please go to Github: github.com/wanglu1209/…