A Guide to FutureBuilder in Flutter

When building Flutter applications, you’ll often need to perform asynchronous operations, like fetching data from a network or reading from a database. A Future is Dart’s way of handling these one-time asynchronous events. But how do you update your UI when the Future completes?

This is where FutureBuilder comes in. It’s a powerful widget that listens to a Future and rebuilds its UI based on the Future’s current state, making it incredibly easy to create reactive and responsive user interfaces.


🤔 What is FutureBuilder?

The FutureBuilder widget is designed to work with a Future. It takes a Future and a builder function as arguments. The builder function is responsible for deciding what to show on the screen based on the Future’s state: whether it’s still waiting, has completed with data, or has failed with an error.

This saves you from manually managing State and calling setState() to update your UI when the Future completes.


🛠️ How to Use FutureBuilder

Using FutureBuilder involves two main parts:

  1. Providing the Future: This is the asynchronous operation you want to listen to.
  2. Implementing the builder function: This function returns a widget based on the latest AsyncSnapshot from the Future.

The builder Function and AsyncSnapshot

The builder function gives you an AsyncSnapshot, which contains information about the Future’s state. Here are the most important properties:

  • snapshot.connectionState: An enum that tells you the current state of the connection. You’ll most often check if it’s ConnectionState.waiting or ConnectionState.done.
  • snapshot.hasData: A boolean that is true if the Future completed successfully with a non-null value.
  • snapshot.hasError: A boolean that is true if the Future failed.
  • snapshot.data: The data returned by the Future upon successful completion.
  • snapshot.error: The error object if the Future failed.

Example: Fetching Data from a Simulated API

Let’s build a simple app that fetches a piece of data and displays it. We’ll simulate a network call with a Future.delayed.

a. Create a Function that Returns a Future

First, let’s define a function that simulates fetching data. This function will return a Future<String> after a 2-second delay.

Future<String> fetchData() async {
  // Simulate a network request
  await Future.delayed(Duration(seconds: 2));
  // Return a piece of data
  return "Data fetched successfully!";
  // To test the error state, you can throw an exception:
  // throw Exception('Failed to fetch data');
}

b. Use FutureBuilder in the UI

Now, we’ll use FutureBuilder to listen to fetchData() and update the UI accordingly.

import 'package:flutter/material.dart';

class MyDataScreen extends StatefulWidget {
  @override
  _MyDataScreenState createState() => _MyDataScreenState();
}

class _MyDataScreenState extends State<MyDataScreen> {
  late Future<String> _dataFuture;

  @override
  void initState() {
    super.initState();
    _dataFuture = fetchData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FutureBuilder Example'),
      ),
      body: Center(
        child: FutureBuilder<String>(
          future: _dataFuture, // The Future to listen to
          builder: (context, snapshot) {
            // 1. Check the connection state
            if (snapshot.connectionState == ConnectionState.waiting) {
              return CircularProgressIndicator();
            }

            // 2. Check for errors
            if (snapshot.hasError) {
              return Text('Error: ${snapshot.error}');
            }

            // 3. Check for data
            if (snapshot.hasData) {
              return Text(
                snapshot.data!,
                style: TextStyle(fontSize: 24),
              );
            }

            // 4. Default case (should not be reached in this example)
            return Text('No data yet.');
          },
        ),
      ),
    );
  }
}

In this example:

  • We call fetchData() in initState() to ensure the Future is only created once.
  • FutureBuilder listens to _dataFuture.
  • The builder function shows a CircularProgressIndicator while waiting, an error message on failure, and the fetched data on success.

✅ When to Use FutureBuilder

FutureBuilder is ideal for:

  • Fetching data from an API when a screen loads.
  • Reading data from a local database or file.
  • Any one-time asynchronous operation where the result is needed to build a part of your UI.

📚 Resources

Happy coding! 🚀