What is a Future?
A Future represents a potential value or error that will be available at some time in the future. It's the core of asynchronous programming in Flutter. When you perform a time‑consuming operation (like a network request, file I/O, or database query) you don't want to block the UI. Instead, you get a Future that will complete when the operation finishes, and you can continue executing other code in the meantime.
Creating a Future
There are several ways to create a Future:
Future.value– creates a future that completes immediately with a value.
Future.error– creates a future that completes immediately with an error.
Future.delayed– creates a future that completes after a delay.
Future.sync– runs a function synchronously and completes with its result.
asyncfunctions – any function markedasyncautomatically returns aFuture.
Using then, catchError, and whenComplete
The traditional way to handle a Future is with .then() for success, .catchError() for errors, and .whenComplete() for cleanup. However, async/await is now preferred for readability.
Async/Await – The Modern Way
async and await make asynchronous code look like synchronous code. Mark a function as async, then use await to wait for a Future to complete. The await expression returns the result of the Future. Errors are caught with try/catch.
FutureBuilder – Reactive UI for Futures
FutureBuilder is a Flutter widget that builds itself based on the state of a Future. It handles the loading, error, and data states for you. You provide a future and a builder that receives a snapshot containing the current connection state, data, and error.
Handling Different Connection States
The snapshot's connectionState tells you the status:
ConnectionState.none: No future provided or the future has not started.
ConnectionState.waiting: The future is still pending (loading).
ConnectionState.active: For streams, but for futures it's rarely used.
ConnectionState.done: The future has completed (success or error).
You can check snapshot.hasData, snapshot.hasError, and snapshot.data to decide what to show.
Combining Multiple Futures
Sometimes you need to wait for several futures to complete. Use Future.wait to run them in parallel and wait for all to complete. It returns a future that completes with a list of results.
Future.any returns the first future that completes (success or error).
Error Handling in Futures
Unhandled errors in a Future can cause crashes. Always handle errors:
- With
async/await: usetry/catch.
- With
- With
.then: use.catchError.
- With
- With
FutureBuilder: checksnapshot.hasErrorand show a user‑friendly message.
- With
Best Practices
- Store the future in the state: Avoid creating the future inside the
buildmethod because it will be recreated on every rebuild. Create it ininitStateor as a variable.
- Store the future in the state: Avoid creating the future inside the
- Use
FutureBuilderfor UI: It manages the subscription and state for you.
- Use
- Handle errors gracefully: Show a user‑friendly message, not just a red screen.
- Consider cancellation: If a future is no longer needed (e.g., widget disposed), consider canceling it. The
futureparameter inFutureBuilderis not automatically canceled; if the future can't be canceled, use aStatefulWidgetand manage the flag.
- Consider cancellation: If a future is no longer needed (e.g., widget disposed), consider canceling it. The
Common Mistakes
- Recreating the future in
build: Causes the future to restart on every rebuild, leading to flickering and performance issues.
- Recreating the future in
- Not checking
connectionState: Showing a loading indicator only whensnapshot.hasDatais false can lead to showing stale data while loading.
- Not checking
- Forgetting to handle errors: Uncaught errors may crash the app.
- Using
setStatewith futures:FutureBuilderis cleaner and avoids manual state management.
- Using
Complete Example
Key Takeaways
Futurerepresents an asynchronous operation that will complete later.
- Use
async/awaitfor clean asynchronous code.
- Use
FutureBuilderbuilds UI based on future state (loading, error, data).
- Combine multiple futures with
Future.waitorFuture.any.
- Combine multiple futures with
- Always handle errors to prevent crashes.
- Store the future outside
buildto avoid unnecessary recreation.
- Store the future outside