What are `FutureBuilder` and `StreamBuilder` for, and when do you use each one?
Show answer — try answering out loud first
Both connect asynchronous data to the UI. FutureBuilder builds the interface from a Future (a single result, like an HTTP request). StreamBuilder builds it from a Stream (multiple values over time, like live data). Each one rebuilds the widget according to the state of the async data (loading, has data, has error).
Instead of handling async state by hand with setState, these widgets do it for you:
FutureBuilder: you give it aFutureand abuilder. Thebuilderreceives asnapshotwith the state. Use it for operations that yield one result: loading a profile, reading a file.StreamBuilder: you give it aStream. It rebuilds every time the stream emits a value. Use it for data that changes continuously: location, chat messages, a live counter.
The snapshot tells you: whether there's an error (hasError), whether there's data (hasData), or whether it's still waiting (connectionState == waiting). With that you decide what to show.
import 'package:flutter/material.dart';
Future<String> loadName() async {
await Future.delayed(const Duration(seconds: 1));
return "Mike";
}
class Profile extends StatelessWidget {
const Profile({super.key});
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: loadName(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator(); // loading
}
if (snapshot.hasError) {
return Text("Error: ${snapshot.error}"); // failed
}
return Text("Hello, ${snapshot.data}"); // ready
},
);
}
}
Creating the Future inside build directly in the future: parameter. Since build runs many times, the async operation would fire again and again on every rebuild. The right way is to create the Future only once: store it in a state variable (in initState) or use a StatefulWidget. The same care applies to the Stream of StreamBuilder.
"
FutureBuilderandStreamBuilderconnect asynchronous data to the UI without me handlingsetStateby hand.FutureBuilderis for a single result, like an HTTP request;StreamBuilderis for data that changes over time, like a chat or a location. Both give me asnapshotwith the state: loading, has data, or has error, and based on that I show a spinner, the content, or an error message. One important thing is not to create theFutureinsidebuild, because it would be relaunched on every rebuild; I create it once ininitState."
Why is it a problem to write future: myAsyncFunction() directly inside build?
See answer
Because build is called many times (on every rebuild), and each time a new Future would be created, relaunching the async operation (for example, repeating the network request) and resetting the loading state. The solution is to create the Future only once, typically by storing it in a variable in initState of a StatefulWidget, and pass that variable to the FutureBuilder.