What is future builder in Flutter

Future Builder is a widget that builds itself. But, it requires a snapshot of interaction with a Future.

How will we get the Future?

We must have obtained the Future in three ways.

They are State.initStateState.didUpdateWidget, or State.didChangeDependencies.

What does it mean?

We may have obtained Future either through change of state, or by change in dependencies.

It means a lot.

Why?

The reason is, we can use FutureBuilder using Scoped Model or Provider. In other Words using the Inherited Widget.

In fact, we are going to do the same thing here.

We will use both Scoped Model and Provider to insert data to a SQLite database. As a result, the FutureBuilder widget will rebuild itself and retrieve the data.

Actually we will execute asynchronous functions. Consequently the asynchronous function will make the User Interface update.

By default FutureBuilder is stateful in nature. It maintains its own state.

How does FutureBuilder work?

The FutureBuilder shows messages like “loading”. It does that based on connection state. And, after that based on new “data”, or “snapshot”, it updates the UI. As a consequence we get a new view.

The advantage of FutureBuilder is, it does not use two “state variables”. When the new “data” arrives, it updates the “view”.

To sum up, the FutureBuilder is a wrapper or boilerplate of what we do.

What is the difference between FutureBuilder and StreamBuilder?

We’ll discuss #StreamBuilder class in a separate article. However, the StreamBuilder class has some similarities with the FutureBuilder class.

Firstly, they listen to changes that occur on their respective objects.

Secondly, after that, they trigger a new construction when they get the notification.

This is the basic functionality that they both practice.

Then, what is the difference?

The object makes the difference. This is the object that they are listening to.

As we told before, #Future is a representation of asynchronous function. As a result, the #Future has one and only answer.

If the #Future is completed successfully, we get the result. If not, we get the error.

On the other hand, the #Stream will get each new value. Even if it had an error, we would get the last value.

Therefore, the main difference is a #Future cannot listen to a change that varies. The #Future has one single answer always.

Enough talking.

Let us view the screenshots first. Viewing the images of our Flutter Applications will clarify the concept.

After that we will discuss the respective code.

When should I use FutureBuilder?

In our first Flutter Application we are going to use the Scoped Model and the Provider with a single FutureBuilder widget. As a result, it does not work properly.

However, the data has been inserted to the SQLite database properly.

Firstly, we will try to insert data to a SQLite database by a Provider object.

Therefore, initially our Flutter Application will look like the following.

Future Builder example one
Future Builder example one

Let us press the Button. According to our code, it should insert two User objects. One from the Provider and the other from the Scoped Model class.

Because the FutureBuilder listens to both objects it should update the UI and display two user names.

But that did not happen.

Future Builder example two with Scoped Mode and Provider
Future Builder example two with Scoped Mode and Provider

The second user’s name will only be displayed when we click the Button below.

Future Builder example two
Future Builder example two

If we click the Button “Add Users by Provider” again, only one name is displayed below the two Users. Although the second user is inserted, it does not reflect on the UI.

Let us check the code where we have mixed the Scoped Model and the Provider.

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

import 'package:scoped_model/scoped_model.dart';

import '/model/user_model.dart';
import '/model/user_prvider.dart';
import '/model/database_handler.dart';
import '/model/user.dart';

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  static const String title = 'Provider, Scoped Model, SQLite';

  @override
  Widget build(BuildContext context) {
    final userModel = ScopedModel.of<UserModel>(context);
    final userProvider = Provider.of<UserProvider>(context);

    final handler = DatabaseHandler();
    Future<int> addUsers() async {
      User firstUser = User(
        name: userModel.userOne.name,
        location: userModel.userOne.location,
      );
      User secondUser = User(
        name: userProvider.userTwo.name,
        location: userProvider.userTwo.location,
      );
      List<User> listOfUsers = [
        firstUser,
        secondUser,
      ];
      return await handler.insertUser(listOfUsers);
    }

    return ScopedModelDescendant<UserModel>(
      builder: (context, child, model) => Scaffold(
        appBar: customAppBar(title),
        body: FutureBuilder(
          future: handler.retrieveUsers(),
          builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
            if (snapshot.hasData) {
              return ListView.builder(
                itemCount: snapshot.data?.length,
                itemBuilder: (BuildContext context, int index) {
                  return Column(
                    children: [
                      Card(
                        child: ListTile(
                          key: ValueKey<int>(snapshot.data![index].id!),
                          contentPadding: const EdgeInsets.all(8.0),
                          title: Text(
                            snapshot.data![index].name,
                            style: const TextStyle(
                              fontSize: 30,
                              color: Colors.red,
                            ),
                          ),
                          subtitle: Text(
                            snapshot.data![index].location,
                            style: const TextStyle(
                              fontSize: 20,
                              color: Colors.blue,
                            ),
                          ),
                        ),
                        elevation: 20,
                      ),
                      TextButton(
                        onPressed: () {
                          handler.initializeDB().whenComplete(() async {
                            await addUsers();
                          });
                          model.addingUsers();
                        },
                        child: const Text(
                          'Add Users by Scoped Model',
                          style: TextStyle(
                            fontSize: 20,
                            fontWeight: FontWeight.w600,
                          ),
                        ),
                      )
                    ],
                  );
                },
              );
            } else {
              return const Center(child: CircularProgressIndicator());
            }
          },
        ),
        floatingActionButton: FloatingActionButton.extended(
          onPressed: () {
            handler.initializeDB().whenComplete(() async {
              await addUsers();
            });
            userProvider.addingUsers();
          },
          label: const Text(
            'Add Users by Provider',
            style: TextStyle(
              fontSize: 25,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
      ),
    );
  }

  AppBar customAppBar(String title) {
    return AppBar(
      centerTitle: true,
      //backgroundColor: Colors.grey[400],
      flexibleSpace: Container(
        decoration: const BoxDecoration(
          gradient: LinearGradient(
            colors: [
              Colors.pink,
              Colors.grey,
            ],
            begin: Alignment.topRight,
            end: Alignment.bottomRight,
          ),
        ),
      ),
      elevation: 20,
      titleSpacing: 80,
      title: Text(
        title,
        textAlign: TextAlign.left,
      ),
    );
  }
}

For full code please visit the respective GitHub Repository.

However, we could have separated them. And that is what we have done for the second Flutter Application.

As a result, the FutureBuilder and our Flutter Application works perfectly.

How do you reset the future builder Flutter?

This time, we will add the first user by using the Scoped Model.

Future Builder example three
Future Builder example three

If we press the Button below it will add the user and it also displays the name and location of User. Moreover, below the display of the newly created User object, we can see the “Navigation Button”.

Future Builder example four
Future Builder example four

Let us see the code first. After that we will click the “Next Page” Button to add another User by the Provider.

Firstly, we see the User Model class that extends the Model class from the Scoped Model dependency.

import 'package:scoped_model/scoped_model.dart';

import 'user.dart';

class UserModel extends Model {
  User _userModel = User(name: 'John Smith', location: 'Back East');
  User get userModel => _userModel;

  void addingUsers() {
    _userModel = userModel;

    notifyListeners();
  }
}

Secondly we will see the FutureBuilder widget that uses the Dependent Scoped User Model to add the Users.

import 'package:flutter/material.dart';

import 'package:scoped_model/scoped_model.dart';

import '/model/user_model.dart';

import '/model/database_handler.dart';
import '/model/user.dart';
import 'provider_home_page.dart';

class ModelHomePage extends StatelessWidget {
  const ModelHomePage({Key? key}) : super(key: key);

  static const String title = 'Adding by Scoped Model';

  @override
  Widget build(BuildContext context) {
    final userModel = ScopedModel.of<UserModel>(context);

    final handler = DatabaseHandler();
    Future<int> addUsers() async {
      User firstUser = User(
        name: userModel.userModel.name,
        location: userModel.userModel.location,
      );

      List<User> listOfUsers = [
        firstUser,
      ];
      return await handler.insertUser(listOfUsers);
    }

    return ScopedModelDescendant<UserModel>(
      builder: (context, child, model) => Scaffold(
        appBar: customAppBar(title),
        body: FutureBuilder(
          future: handler.retrieveUsers(),
          builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
            if (snapshot.hasData) {
              return ListView.builder(
                itemCount: snapshot.data?.length,
                itemBuilder: (BuildContext context, int index) {
                  return Column(
                    children: [
                      Card(
                        child: ListTile(
                          key: ValueKey<int>(snapshot.data![index].id!),
                          contentPadding: const EdgeInsets.all(8.0),
                          title: Text(
                            snapshot.data![index].name,
                            style: const TextStyle(
                              fontSize: 30,
                              color: Colors.red,
                            ),
                          ),
                          subtitle: Text(
                            snapshot.data![index].location,
                            style: const TextStyle(
                              fontSize: 20,
                              color: Colors.blue,
                            ),
                          ),
                        ),
                        elevation: 20,
                      ),
                      TextButton(
                        onPressed: () {
                          Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (context) => const ProviderHomePage(),
                            ),
                          );
                        },
                        child: const Text(
                          'Next Page, Add by Provider',
                          style: TextStyle(
                            fontSize: 20,
                            fontWeight: FontWeight.w600,
                          ),
                        ),
                      )
                    ],
                  );
                },
              );
            } else {
              return const Center(child: CircularProgressIndicator());
            }
          },
        ),
        floatingActionButton: FloatingActionButton.extended(
          onPressed: () {
            handler.initializeDB().whenComplete(() async {
              await addUsers();
            });
            userModel.addingUsers();
          },
          label: const Text(
            'Add Users by Model',
            style: TextStyle(
              fontSize: 25,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
      ),
    );
  }

  AppBar customAppBar(String title) {
    return AppBar(
      centerTitle: true,
      //backgroundColor: Colors.grey[400],
      flexibleSpace: Container(
        decoration: const BoxDecoration(
          gradient: LinearGradient(
            colors: [
              Colors.pink,
              Colors.grey,
            ],
            begin: Alignment.topRight,
            end: Alignment.bottomRight,
          ),
        ),
      ),
      elevation: 20,
      titleSpacing: 80,
      title: Text(
        title,
        textAlign: TextAlign.left,
      ),
    );
  }
}

Next, we go the next page where we can add the second User by using the Provider.

Future Builder example five
Future Builder example five

The next page correctly shows that one User has already been added to the SQLite database.

Now, when we add the Button “Add Users by Provider”, another user is added. Moreover, two users are displayed correctly on the UI.

Future Builder example six
Future Builder example six

Let us take a look at the code now.

Firstly we will take a look at the User Provider class that extends the ChangeNotifier. After that it notifies the listeners.

import 'package:flutter/material.dart';

import 'user.dart';

class UserProvider with ChangeNotifier {
  User _userProvider = User(name: 'Json Web', location: 'Detroit');
  User get userProvider => _userProvider;

  void addingUsers() {
    _userProvider = userProvider;

    notifyListeners();
  }
}

Secondly we will see the FutureBuilder that rebuilds when the User object changes its state.

We have created another FutureBuilder for this page, so that we can add users by Provider.

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

import '/model/user_prvider.dart';
import '/model/database_handler.dart';
import '/model/user.dart';

class ProviderHomePage extends StatelessWidget {
  const ProviderHomePage({Key? key}) : super(key: key);

  static const String title = 'Adding By Provider';

  @override
  Widget build(BuildContext context) {
    final userProvider = Provider.of<UserProvider>(context);

    final handler = DatabaseHandler();
    Future<int> addUsers() async {
      User secondUser = User(
        name: userProvider.userProvider.name,
        location: userProvider.userProvider.location,
      );
      List<User> listOfUsers = [
        secondUser,
      ];
      return await handler.insertUser(listOfUsers);
    }

    return Scaffold(
      appBar: AppBar(
        title: const Text(title),
      ),
      body: FutureBuilder(
        future: handler.retrieveUsers(),
        builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
          if (snapshot.hasData) {
            return ListView.builder(
              itemCount: snapshot.data?.length,
              itemBuilder: (BuildContext context, int index) {
                return Column(
                  children: [
                    Card(
                      child: ListTile(
                        key: ValueKey<int>(snapshot.data![index].id!),
                        contentPadding: const EdgeInsets.all(8.0),
                        title: Text(
                          snapshot.data![index].name,
                          style: const TextStyle(
                            fontSize: 30,
                            color: Colors.red,
                          ),
                        ),
                        subtitle: Text(
                          snapshot.data![index].location,
                          style: const TextStyle(
                            fontSize: 20,
                            color: Colors.blue,
                          ),
                        ),
                      ),
                      elevation: 20,
                    ),
                  ],
                );
              },
            );
          } else {
            return const Center(child: CircularProgressIndicator());
          }
        },
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          handler.initializeDB().whenComplete(() async {
            await addUsers();
          });
          userProvider.addingUsers();
        },
        label: const Text(
          'Add Users by Provider',
          style: TextStyle(
            fontSize: 25,
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
    );
  }
}

In the second Flutter Application we have successfully used The Scoped Model and the Provider. However, we have implemented the FutureBuilder widget separately.

For the full code snippet for this Flutter Application, please visit the respective GitHub Repository.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

Courses at Educative

GitHub repository

Technical blog

Twitter

Comments

One response to “What is future builder in Flutter”

  1. […] convert we need the Future Builder Widget that will use the List […]

Leave a Reply