0
0
FlutterHow-ToBeginner · 3 min read

How to Use FutureBuilder in Flutter: Simple Guide

Use FutureBuilder in Flutter to build widgets based on the state of a Future. Provide a future and a builder function that returns different UI depending on whether the future is loading, done, or has an error.
📐

Syntax

The FutureBuilder widget takes a future and a builder function. The builder receives a BuildContext and an AsyncSnapshot which holds the current state and data of the future.

Use snapshot.connectionState to check if the future is waiting, done, or active, and snapshot.hasData or snapshot.hasError to handle results or errors.

dart
FutureBuilder<T>({
  Key? key,
  required Future<T>? future,
  required AsyncWidgetBuilder<T> builder,
})
💻

Example

This example shows how to use FutureBuilder to fetch a string after a delay and display it. It shows a loading spinner while waiting and the result when done.

dart
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  Future<String> fetchData() async {
    await Future.delayed(const Duration(seconds: 2));
    return 'Hello from Future!';
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('FutureBuilder Example')),
        body: Center(
          child: FutureBuilder<String>(
            future: fetchData(),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return const CircularProgressIndicator();
              } else if (snapshot.hasError) {
                return Text('Error: ${snapshot.error}');
              } else if (snapshot.hasData) {
                return Text(snapshot.data!);
              } else {
                return const Text('No data');
              }
            },
          ),
        ),
      ),
    );
  }
}
Output
App shows a loading spinner for 2 seconds, then displays: Hello from Future!
⚠️

Common Pitfalls

  • Not providing a future or recreating it inside build causes the future to restart repeatedly.
  • Not handling all connectionState cases can cause UI glitches.
  • Ignoring errors in the future leads to unhandled exceptions.

Always assign the future outside the build method or cache it to avoid repeated calls.

dart
/* Wrong: future recreated every build */
FutureBuilder<String>(
  future: fetchData(), // called every build, restarts future
  builder: (context, snapshot) => ...,
)

/* Right: future stored once */
class MyWidget extends StatefulWidget {
  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  late Future<String> _myFuture;

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

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String>(
      future: _myFuture,
      builder: (context, snapshot) => ...,
    );
  }
}
📊

Quick Reference

  • future: The asynchronous operation to watch.
  • builder: Function to build UI based on snapshot state.
  • snapshot.connectionState: Use to check loading, done, or none.
  • snapshot.hasData: True if future completed with data.
  • snapshot.hasError: True if future completed with error.

Key Takeaways

Use FutureBuilder to update UI based on asynchronous Future results.
Provide a stable Future to avoid restarting the async call on every build.
Check snapshot.connectionState to handle loading, done, and error states.
Always handle errors to avoid app crashes.
Use FutureBuilder inside widgets that rebuild when async data changes.