FutureBuilder, StreamBuilder and the alternatives
FutureBuilder and StreamBuilder display async data directly in the widget tree: a snapshot carries the state (connectionState, hasData, hasError).
Their biggest trap: if you create the future inside build, every rebuild fires a new request.
``` // no: a new request on every rebuild FutureBuilder(future: api.load(), ...)
// yes: the future is created once late final _future = api.load(); FutureBuilder(future: _future, ...) ```
This bug tends to surface late, because the screen still works — it just makes dozens of requests in the background.
| Tool | Where it fits | Weakness |
|---|---|---|
| `FutureBuilder` | A one-off load on a simple screen | The recreated-future trap; retry is awkward |
| `StreamBuilder` | A continuous stream: real-time data, notifications | You must mind the subscription lifetime |
| State management (Riverpod/Bloc) | Retry, caching, shared state | An extra layer and setup cost |
Interview tip. The strong answer to "when do you use FutureBuilder?" draws the line: for a simple one-off load, yes; once you need retry, caching, shared state or use across screens, move to state management. Always mention the trap: a future created inside build means a new request on every rebuild. Naming that detail signals real experience.
📚 Sources and documentation
- FutureBuilder classofficialapi.flutter.dev
The docs explain outright why creating the future inside `build` is wrong.
- StreamBuilder classofficialapi.flutter.dev
- Networkingofficialdocs.flutter.dev