FutureBuilder in Flutter

Elegantly handle asynchronous operations and loading states

Download Complete Examples

What is FutureBuilder?

The FutureBuilder widget in Flutter is essential for working with asynchronous operations, letting you build the user interface based on a Future's state.

Waiting
Active
Done
Error

States managed by FutureBuilder:

  • Waiting: The Future hasn't completed
  • Active: The Future is in progress (with optional data)
  • Done: The Future completed successfully
  • Error: The Future failed with an error
  • None: There's no associated Future

When to use FutureBuilder?

API Calls

To load data from web services and REST APIs

Database Queries

Read/write operations in databases

File Downloads

Downloading and processing files

Heavy Computations

Operations that require processing time

Basic Implementation

FutureBuilder basic to load API data
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FutureBuilder Example',
      home: const ApiExample(),
    );
  }
}

class ApiExample extends StatelessWidget {
  const ApiExample({super.key});

  // Future que simula una llamada a API  Future<String> fetchUserData() async {
    await Future.delayed(const Duration(seconds: 2)); // Simula delay de red    // En una app real, aquí iría la llamada HTTP real    return 'Datos del usuario cargados exitosamente';
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('FutureBuilder Example')),
      body: FutureBuilder<String>(
        future: fetchUserData(),
        builder: (context, snapshot) {
          // Verificar el estado del Future          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          } else if (snapshot.hasError) {
            return Center(
              child: Text('Error: ${snapshot.error}'),
            );
          } else if (snapshot.hasData) {
            return Center(
              child: Text('Datos: ${snapshot.data}'),
            );
          } else {
            return const Center(
              child: Text('No hay datos disponibles'),
            );
          }
        },
      ),
    );
  }
}

Properties of FutureBuilder

future

The Future that will be observed and awaited

Type: Future<T>?

Required: Yes (can be null)

builder

Function that builds the widget based on the snapshot

Type: Widget Function(BuildContext, AsyncSnapshot<T>)

Required: Yes

initialData

Initial data while the Future completes

Type: T?

Default: null

AsyncSnapshot Properties

connectionState

Current connection state of the Future

Values: none, waiting, active, done

hasData

Indicates whether the snapshot contains data

Type: bool

data

The data received from the Future

Type: T?

hasError

Indicates whether the Future failed with an error

Type: bool

error

The error object if the Future failed

Type: Object?

States of ConnectionState

ConnectionState.none

There's no associated Future

future: null

ConnectionState.waiting

Future pending, no data yet

!snapshot.hasData

ConnectionState.active

Future in progress, may have data

snapshot.hasData (optional)

ConnectionState.done

Future completed (success or error)

snapshot.hasData || snapshot.hasError

Visual Examples

1. Loading User Data

Perfil de Usuario

Loading user data...

Error loading the data

FutureBuilder<User>(
  future: userRepository.getUser(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CircularProgressIndicator(),
            SizedBox(height: 16),
            Text('Loading user data...'),
          ],
        ),
      );
    } else if (snapshot.hasError) {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(Icons.error, size: 64, color: Colors.red),
            SizedBox(height: 16),
            Text('Error al cargar los datos'),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                // Recargar los datos                setState(() {});
              },
              child: Text('Retry'),
            ),
          ],
        ),
      );
    } else if (snapshot.hasData) {
      final user = snapshot.data!;
      return UserProfile(user: user);
    } else {
      return Center(child: Text('No hay datos disponibles'));
    }
  },
)

2. List with Initial Data

Lista de Productos
Laptop Gaming - \$1200
Smartphone - \$800
Audífonos - \$150
FutureBuilder<List<Product>>(
  future: productService.getProducts(),
  initialData: const [], // Lista vacía como datos iniciales  builder: (context, snapshot) {
    final products = snapshot.data ?? [];
    
    if (snapshot.connectionState == ConnectionState.waiting && products.isEmpty) {
      return ListView.builder(
        itemCount: 3,
        itemBuilder: (context, index) {
          return ListTile(
            leading: CircleAvatar(backgroundColor: Colors.grey[300]),
            title: Container(
              height: 16,
              width: 100,
              color: Colors.grey[300],
            ),
            subtitle: Container(
              height: 12,
              width: 60,
              color: Colors.grey[300],
            ),
          );
        },
      );
    }
    
    return ListView.builder(
      itemCount: products.length,
      itemBuilder: (context, index) {
        final product = products[index];
        return ListTile(
          leading: Icon(Icons.shopping_bag),
          title: Text(product.name),
          subtitle: Text('\$${product.price}'),
          trailing: Icon(Icons.arrow_forward_ios),
        );
      },
    );
  },
)

Interactive Demo of FutureBuilder

Demo FutureBuilder

Ready to run

Press "Run Future" to start

// El código se generará aquí

Best Practices

✅ What you SHOULD do

  • Use initialData to show content while loading
  • Handle all states: waiting, done, error
  • Use ConnectionState for precise states
  • Provide visual feedback during loading
  • Implement retries for network errors

❌ What you should NOT do

  • Don't call setState inside the builder
  • Don't create the Future inside the build method
  • Don't ignore the error state
  • Don't use FutureBuilder for streams (use StreamBuilder)
  • Don't forget to handle the null data case

Pro Tip

For operations that can run multiple times, consider using FutureBuilder with a unique Key that changes when you need to reload the data. This forces the FutureBuilder to be recreated and the Future to run again.

Exercises to Practice

Exercise 1: Weather App

Create an app that shows current weather using a public API.

Suggested API: OpenWeatherMap

States to handle: Loading, weather data, connection error

Exercise 2: News List

Implement a news reader that loads articles from an API.

Features: Infinite scroll, skeleton loading, pull to refresh

Exercise 3: Image Gallery

Create a gallery that loads images from a web service.

Features: Image grid, progressive loading, per-image error handling

Common Troubleshooting

❌ The Future runs multiple times

Solution: Move the Future's creation outside the build method

// ❌ WRONG: In the build methodfuture: miFuture() // ✅ RIGHT: In initState or a class variableFuture<String> miFuture = obtenerDatos();

❌ Doesn't update when the data changes

Solution: Use a unique Key or call setState with a new Future

FutureBuilder( key: ValueKey(uniqueId), // Fuerza recreación future: miFuture, // ... )

❌ Error: "setState() called during build"

Solution: Don't call setState inside the FutureBuilder's builder

// Usa un callback o método separadoonPressed: () => _recargarDatos()

Additional Resources

📚 Official Documentation

See the complete FutureBuilder documentation

View Documentation

🎥 Video Tutorial

Learn with practical examples of FutureBuilder

View Tutorial

💡 Advanced Examples

Discover complex implementations with real APIs

Explore Examples