I have been using a FutureBuilder to read data from an api, and update the UI when it completes. All good, but now I want to refresh this every 5 minutes. I thought I could do this with a Timer. My code
class _MyTileState extends State<MyTile> {
late Future<MyData?> myFuture = fetchMyData();
@override
Widget build(BuildContext context) {
return Column(
children: [
FutureBuilder<MyData?>(
future: myFuture,
builder: (BuildContext context, AsyncSnapshot<MyData?> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return Text(snapshot.data.title);
}
},
),
],
);
}
Future<MyData?> fetchMyData() async {
var myData = await readMyData();
return myData;
}
If I add a timer I get an error message : The instance member ‘fetchMyData’ can’t be accessed in an initializer
Timer timer = Timer(Duration(minutes: 5), () {
myFuture = fetchMyData();
setState(() {});
});
While trying to sort this, it has become apparent that FutureBuilder should not be used to refresh. Instead use a StreamBuilder. But this just displays the CircularProgressIndicator and only appears to run once
StreamBuilder<MyData>(
stream: fetchMyDataStream(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
return Text(snapshot.data.title);
}
return CircularProgressIndicator();
}
),
Stream<MyData> get fetchMyDataStream() async* {
print("start fetch my data stream");
await Future.delayed(Duration(seconds: 5)); // set to seconds for testing
var myData = await readMyData();
yield myData;
}
}
I am trying to work out which way I should be going – FutureBuilder or StreamBuilder? And for the preferred option, what I am doing wrong.
2
Answers
You can use Stream Builder and Stream Controller for that. There are many other ways to do so but this is one of the answer
You may simplify it: