Reputation: 2511
How do I cancel FutureBuilder
operation when rebuilding the widget
Lets say I have a code like this... Every time i pressed the Floating button the widget rebuilds calling myFuture
which waits five seconds and then the counter increments... Now I want that during that five seconds if I pressed the Floating button the current Future (which is still is delayed) should stop its operation and the new Future will be called...So at the end I should get a counter of 2 but instead I get 3...
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var counter = 0;
myFuture()async{
await Future.delayed(Duration(seconds:5));
counter++;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: FutureBuilder(
future: myFuture(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return Center(child: CircularProgressIndicator());
else return Text(counter.toString());
}
),
floatingActionButton: FloatingActionButton(
onPressed: ()=>setState(() {}),
child: Icon(Icons.add),
),
);
}
}
Upvotes: 2
Views: 2636
Reputation: 459
Alternatively you can use a Stream
and StreamBuilder
instead of Future
and FutureBuilder
if that fits your use case.
void main() {
// keep a reference to your stream subscription
StreamSubscription<List> dataSub;
// convert the Future returned by getData() into a Stream
dataSub = getData().asStream().listen((List data) {
updateDisplay(data);
});
// user navigated away!
dataSub.cancel();
}
source: https://dart.academy/how_cancel_future/
Upvotes: 0
Reputation: 4836
In order to cancel a Future, you can use the CancelableOperation
from the async
package.
It's implementation would look like the following :
Future<dynamic> executeCancelable(Future<dynamic> futureMethod) async {
operation?.cancel();
operation = CancelableOperation.fromFuture(futureMethod, onCancel: () {
print('Future stopped');
});
return operation.value;
}
Future<dynamic> futureMethod() async {
return Future.delayed(const Duration(milliseconds: 3000), () {
return counter++;
});
}
Which can be called with the following method :
executeCancelable(futureMethod())
Note that in this example, I'm using a Future.delayed wich can't "really" be cancelled as explained here.
This snippet would work well with an API query for example.
Upvotes: 2
Reputation: 12383
int counter = 0;
fiveSeconds() async {
await Future.delayed(Duration(seconds: 5));
}
twoSeconds() async {
await Future.delayed(Duration(seconds: 2));
}
bool _futureTime = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: FutureBuilder(
future: _futureTime ? fiveSeconds() : twoSeconds(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return Center(child: CircularProgressIndicator());
else counter++;
return Text(counter.toString());
}
),
floatingActionButton: FloatingActionButton(
onPressed: (){setState(() {
_futureTime = !_futureTime;
});
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Upvotes: 0