Fixing a common FutureBuilder and StreamBuilder problem
Why Stack Overflow recommends it
Stack Overflow users link it whenever a Flutter question shows a FutureBuilder or StreamBuilder that refetches or reloads because the future or stream is created inside build().
Recommended by 9 people across 31 different months, 2021 to 2025.
What it is
A short Flutter and Dart screencast by Randal Schwartz showing a common bug where a FutureBuilder or StreamBuilder restarts its async work on every rebuild. He explains that the future or stream should be created once and kept in a state variable instead of being built inside the builder's argument.
What people said
Strong rec Dhafin Rayhan · 2024-01 · Stack Overflow · on “Flutter Opening they keyboard causes widget to rebuild”
Widget rebuild is expected as the screen size changes when you open the keyboard, so you should not prevent that. But instead, you should make the stream a variable in your state and initialize it inside initState : class _CommentPageState extends State<CommentPage> { late Stream<QuerySnapshot> _commentStream; // ... @override void initState() { // ... _commentStream = _getCommentStream(); } and then you can use it like this: child: StreamBuilder<QuerySnapshot>( stream: _commentStream, // ... ) This way, _getCommentStream() won't get called again when the widget rebuilds. This problem is addressed in Flutter's Widget of the Week video of FutureBuilder [LINK] (and that's why they had to do a
Recommends Randal Schwartz · 2021-03 · Stack Overflow · on “Flutter Futurebuilder data sorting only update after hot reload”
Don't create the future in the "future:" argument of FutureBuilder. As said in [LINK] : The future must have been obtained earlier, e.g. during State.initState, State.didUpdateWidget, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.build method 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. I illustrate this in my short screencast at [this video] .
Recommends Randal Schwartz · 2022-07 · Stack Overflow · on “Count sum of items using future”
This is broken. Not sure if it's your *only* problem: `future: get_cart_no(),`. Never create the Future for a FutureBuilder as the future: parameter. Read the first few paragraphs of the FutureBuilder docs for why not, or watch my video on this: [this video]
Recommends Randal Schwartz · 2022-07 · Stack Overflow · on “How Update the data shown in one page after changing the data in database from the second page”
This is broken: future: FirebaseFirestore.instance.collection('users').doc(FirebaseAuth.instance.currentUser!.uid).get(), for reasons given in the first few paragraphs of FutureBuilder documentation, and illustrated in my oft-referenced video [this video] . In brief, you cannot build a future as the future: parameter of a FutureBuilder . You need to lift it out into State.
Recommends Md. Yeasin Sheikh · 2022-08 · Stack Overflow · on “Flutter & Firebase: reduce the number of reads with StreamBuilder”
For StatefulWidget stream api will gets call every state changes. Create a state variable for stream, late final myStream = FirebaseFirestore.instance .collection('user') .orderBy('total_score', descending: true) .limit(3) .snapshots(includeMetadataChanges: false); @override Widget build(BuildContext context) { and use it on StreamBuilder<QuerySnapshot>( stream: myStream You can check Fixing a common FutureBuilder and StreamBuilder problem [this video]
Recommends Dani · 2022-08 · Stack Overflow · on “Flutter: TabBar with FutureBuilder or StreamBuilder”
The problem comes with the way I declare the stream. This video explains it very well: [this video] So the change would be something like this: class BottomBarState extends State<BottomBar> with TickerProviderStateMixin { var portfoliosStream; @override void initState() { super.initState(); portfoliosStream = FirebaseProfile().getPortfolios(); } @override Widget build(BuildContext context) { super.build(context); return Container( child: tabs(), ); } return StreamBuilder<QuerySnapshot>( stream: portfoliosStream, builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) { if (snapshot.hasError) return Text('Error: ${snapshot.error}'); if (!snapshot.hasData) return const Loader(); if (
Recommends Randal Schwartz · 2022-08 · Stack Overflow · on “StreamBuilder not returning CircularProgressIndicator when waiting for Firebase connection”
DON'T DO THIS: `stream: FirebaseFirestore.instance.collection('posts').snapshots()`. Never create your stream as the stream parameter to a StreamBuilder. For details, look at the first two paragraphs of FutureBuilder docs, or watch this video: [this video]
Recommends Frank van Puffelen · 2022-10 · Stack Overflow · on “Why does futurebuilder keep fetching data everytime TextFormField is pressed?”
My guess is that your GetDocumentFromFirebase function does a new get() call to Firestore each time it runs, which happens every time the widget is rendered. If you want to prevent refetching the document every time, put the code in a stateful widget, and keep the Future<DocumentSnapshot> in a state variable that you only initialize once. Also see Randal's excellent explanation on Fixing a common FutureBuilder and StreamBuilder problem [this video]
Recommends Randal Schwartz · 2023-05 · Stack Overflow · on “Futurebuilder not updating with the future value as it may be initializing before the data is ready”
This: Stream<List<DocumentSnapshot>> stream = geo.collection(collectionRef: ref).within(center: center, radius: radius, field: field); You're creating the stream in the build. Every time the widget rebuilds (which could be 60 to 120 times per second, especially if there's an animation), the stream will be restarted . This means you'll be forever loading, and rapidly burning through your firebase free tier. (Ouch!) The solution is to lift that stream into the initState of this widget, or use something like a Riverpod StreamProvider to wrap the value so it gets created only once, and then accessed on multiple build passes. I have a video that illustrates the problem better at [this video] .
Recommends Rohit Kharche · 2023-06 · Stack Overflow · on “Prevent rebuilding of FutureBuilder from child widgets”
As @Randal Schwartz mentioned in the comments and the video he shared, do not build the Stream or Future Builder inside the build Widget as it will rebuild every time when a state changes. Instead you should consider moving the constructed Future to a class instance variable as follows : class GetDeploymentDetails extends StatelessWidget { const GetDeploymentDetails({required this.user, super.key}); final User? user; final constructed_future = FirebaseFirestore.instance .collection('deployments') .where("users", arrayContains: user!.email) .get() .then((value) => Deployment.fromJson(value.docs[0].data())); @override Widget build(BuildContext context) { return FutureBuilder<Deployment>( futur