How to Implement the Future Builder in Flutter

In this guide, we will learn how to implement the Future Builder in Flutter. Future is the operations that take time to perform and return the result later. To handle this problem, we use Asynchronous functions. Asynchronous operations let us program to continue other operations while the current operation is being performed. Dart uses Future objects (futures) to represent the results of asynchronous operations. To handle these operations, we can use Async/await, but it is not possible to integrate async and await on widgets. So it is quite tricky to handle futures in widgets. To solve this problem flutter provided a widget called Future Builder.

In the future builder, it calls the future function to wait for the result, and as soon as it produces the result it calls the builder function where we build the widget.

Now we are fetching the data of some APIs with the help of Future Builder. For these, we need an external Package name http to get the request from the server.

Installation

Run this command:

With Dart:

 $ dart pub add http

With Flutter:

 $ flutter pub add http

This will add a line like this to your package’s pubspec.yaml (and run an implicit dart pub get):

dependencies:
  http: ^0.13.5

Alternatively, your editor might support dart pub get or flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:http/http.dart';

Now we will use the API of reqres to fetch the data of the user. we will create the model of these which look like these

class Datum {
  Datum({
    required this.id,
    required this.email,
    required this.firstName,
    required this.lastName,
    required this.avatar,
  });

  final int id;
  final String email;
  final String firstName;
  final String lastName;
  final String avatar;

  factory Datum.fromJson(Map<String, dynamic> json) => Datum(
        id: json["id"],
        email: json["email"],
        firstName: json["first_name"],
        lastName: json["last_name"],
        avatar: json["avatar"],
      );
}

For the repository we have

import 'dart:convert';

import 'package:http/http.dart' as http;
import 'package:vlogpost/model/data_model.dart';

class ApiService {
  Future<List<Datum>> getDataModel() async {
    final res = await http.get(
      Uri.parse("https://reqres.in/api/users?page=2"),
    );
    if (res.statusCode == 200) {
      final List result = jsonDecode(res.body)['data'];
      return result.map((e) => Datum.fromJson(e)).toList();
    } else {
      throw Exception(res.reasonPhrase);
    }
  }
}

Now for showing on the UI Screen the following code work which look like

import 'package:flutter/material.dart';
import 'package:vlogpost/model/data_model.dart';
import 'package:vlogpost/service/data_api.dart';

class FutureBuilderScreen extends StatefulWidget {
  const FutureBuilderScreen({Key? key}) : super(key: key);

  @override
  _FutureBuilderScreenState createState() => _FutureBuilderScreenState();
}

class _FutureBuilderScreenState extends State<FutureBuilderScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        title: Text(
          "Future Builder in Flutter",
          style: TextStyle(
            color: Colors.black,
            fontWeight: FontWeight.bold,
            fontSize: Theme.of(context).textTheme.headline5?.fontSize,
          ),
        ),
      ),
      body: FutureBuilder(
        future: ApiService().getDataModel(),
        builder: (_, AsyncSnapshot snapshot) {
          final List<Datum>? user = snapshot.data;
          if (!snapshot.hasData) {
            return const Center(child: CircularProgressIndicator());
          }
          return ListView.separated(
            separatorBuilder: (_, __) => const Padding(
              padding: EdgeInsets.all(10.0),
              child: Divider(
                color: Colors.grey,
                height: 10,
              ),
            ),
            itemCount: user!.length,
            itemBuilder: (_, index) {
              return Card(
                margin: const EdgeInsets.all(10),
                child: ListTile(
                  leading: CircleAvatar(
                    radius: 30,
                    backgroundColor: Colors.indigo,
                    backgroundImage: NetworkImage(user[index].avatar),
                  ),
                  title: Text(
                    user[index].firstName,
                    style: const TextStyle(fontSize: 18, color: Colors.black),
                  ),
                  subtitle: Text(
                    user[index].email,
                    style: const TextStyle(fontSize: 16, color: Colors.black38),
                  ),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

AsyncSnapshot has 3 states:

  1. connectionState.none = In this state future is null.The [AsyncSnapshot.data] will be set to [initialData] unless a future has previously been completed, in which case the previous result persists.
  2. connectionState.waiting = [future] is not null, but has not yet completed. The [AsyncSnapshot.data] will be set to [initialData] unless a future has previously been completed, in which case the previous result persists.
  3. connectionState.done = [future] is not null, and has completed. If the future is completed successfully, the [AsyncSnapshot.data] will be set to the value to which the future is completed. If it is completed with an error, [AsyncSnapshot.hasError] will be true and [AsyncSnapshot.error] will be set to the error object.

Meanwhile, when we run this application in the emulator or device, we should get the UI similar to the screenshot below:

Get the full snippet from here

Also Read: Flutter Bloc (V8): How To Fetch Data From An API?