I am loading data when widget is loading like the code below. Once the UI is fully loaded, I like to add one refresh button to reload the data again.
How can I refresh the view ?
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
var futureBuilder = new FutureBuilder(
future: _getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return new Text('loading...');
default:
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else
return createListView(context, snapshot);
}
},
);
return new Scaffold(
appBar: new AppBar(
title: new Text("Home Page"),
),
body: futureBuilder,
);
}
Future<List<String>> _getData() async {
var values = new List<String>();
await new Future.delayed(new Duration(seconds: 5));
return values;
}
Widget createListView(BuildContext context, AsyncSnapshot snapshot) {
}
}
FutureBuilder is a Widget that will help you to execute some asynchronous function and based on that function's result your UI will update. FutureBuilder is Stateful by nature i.e it maintains its own state as we do in StatefulWidgets. Syntax: FutureBuilder( Key key, this. future, this.
Both StreamBuilder and FutureBuilder have the same behavior: They listen to changes on their respective object. And trigger a new build when they are notified of a new value. So in the end, their differences are how the object they listen to works. Future is like Promise in JS or Task in c#.
Widget createListView(BuildContext context, AsyncSnapshot snapshot) {
RaisedButton button = RaisedButton(
onPressed: () {
setState(() {});
},
child: Text('Refresh'),
);
//.. here create widget with snapshot data and with necessary button
}
I did a deep dive into this and it's not that difficult. The builder is properly rebuilt on changing the future (if you trigger the change with setState
). Problem is, the hasData
and hasError
aren't reset until the response is back. But we can use connectionState
instead.
final builder = FutureBuilder(
future: _future,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return _buildLoader();
}
if (snapshot.hasError) {
return _buildError();
}
if (snapshot.hasData) {
return _buildDataView();
}
return _buildNoData();
});
Here's a post on the issue and a linked repo showing the issue and solution: https://www.greycastle.se/reloading-future-with-flutter-futurebuilder/
what i did an it worked for me, is to call the future function again in setState(). in your example it will looks like this.
first you assign your _getData() future function to a variable (_myData) with the same return type, after that, you can override it's value in setState() that will rebuild the UI and therefor run the future again.
in code it will looks like this.(from you example):
class _MyHomePageState extends State<MyHomePage> {
Future<List<String>> _myData = _getData(); //<== (1) here is your Future
@override
Widget build(BuildContext context) {
var futureBuilder = new FutureBuilder(
future: _myData; //<== (2) here you provide the variable (as a future)
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return new Text('loading...');
default:
if (snapshot.hasError)
return Column(
children: [
Icon(Icons.error),
Text('Failed to fetch data.'),
RaisedButton(
child: Text('RETRY'),
onPressed: (){
setState(){
_myData = _getData(); //<== (3) that will trigger the UI to rebuild an run the Future again
}
},
),
],
);
else
return createListView(context, snapshot);
}
},
);
return new Scaffold(
appBar: new AppBar(
title: new Text("Home Page"),
),
body: futureBuilder,
);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With