When to use FutureBuilder in Flutter

1,119

Solution 1

FutureBuilder is used to handle "asynchronous" calls that return the data you want to display.

For example, let's say we have to get a List<String> from the server.

The API call :

  Future<List<String>> getStringList() async {
    try {
      return Future.delayed(Duration(seconds: 1)).then(
        (value) => ['data1', 'data2', 'data3', 'data4'],
      );
    } catch (e) {
      throw Exception(e);
    }
  }

How can we handle the above API call status (load, data, error...etc) using FutureBuilder:

FutureBuilder<List<String>?>(
        future: getStringList(),
        builder: (context, snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.waiting:
              return Center(
                child: CircularProgressIndicator(),
              );
            case ConnectionState.done:
              if (snapshot.hasError)
                return Text(snapshot.error.toString());
              else
                return ListView(
                  children: snapshot.data!.map((e) => Text(e)).toList(),
                );

            default:
              return Text('Unhandle State');
          }
        },
      ),

As we can see, we don't have to create a state class with variable isLoading bool and String for error...etc. FutureBuilder saves us time.

BUT

Since FutureBuilder is a widget and lives in the widget tree, rebuilding can make FutureBuilder run again.

So I would say you can use FutureBuilder when you know there won't be a rebuild, however there are many ways (in the internet) to prevent FutureBuilder from being called again when the rebuild happens but it didn't work for me and leads to unexpected behavior.

Honestly I prefer handling the state in a different class with any state management solution than using FutureBuilder because it would be safer (rebuild wont effect it), more usable and easier to read (spreating business logic from UI).

Solution 2

Actually, you will never need to use FutureBuilder Widget if you don't want to. Your logic in your code do exactly what FutureBuilder Widget does if you optimise FutureBuilder Widget correctly.

This code is exactly same with yours:

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool done = false;
  late Future myFuture;

  @override
  void initState() {
    myFuture = wait();
    super.initState();
  }

  Future<bool> wait() async {
    await Future.delayed(const Duration(seconds: 2));
    return true;
  }

  @override
  Widget build(BuildContext context) {
    print('is built');
    return FutureBuilder(
        future: myFuture,
        builder: (BuildContext context, snapshot) {
           if(snapshot.connectionState == ConnectionState.waiting) {
              return const Scaffold(body: CircularProgressIndicator(),);
           } else {
              return Scaffold(
                 body: Center(
                    child: Column(
                       mainAxisAlignment: MainAxisAlignment.center,
                       children: <Widget>[
                          const Text(
                             'You have pushed the button this many times:',
                          ),
                          Text(
                              '',
                              style: Theme.of(context).textTheme.headline4,
                          ),
                       ],
                     ),
                   ),
                floatingActionButton: FloatingActionButton(
                   onPressed: () {},
                   tooltip: 'Increment',
                   child: const Icon(Icons.add),
                ),
             );
           }
        }
     );
  }
}

Solution 3

FutureBuilder

Widget that builds itself based on the latest snapshot of interaction with a Future.

The future must have been obtained earlier, e.g. during State.initState, State.didUpdateWidget, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder.

If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder's parent is rebuilt, the asynchronous task will be restarted.A general guideline is to assume that every build method could get called every frame, and to treat omitted calls as an optimization.

Documentation is very great way to get started and understand what widget does what in what condition...

https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

Share:
1,119
user14624595
Author by

user14624595

Currently, a flutter enthusiast/developer.

Updated on December 31, 2022

Comments

  • user14624595
    user14624595 10 months

    I would like to know when to use a FutureBuilder and how is it usefull, given the fact that a widget can be built multiple times during its life time and not only when we setState or update it, so instead of using below code:

    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      bool done = false;
    
      @override
      void initState() {
        wait();
        super.initState();
      }
    
      Future<void> wait() async {
        await Future.delayed(Duration(seconds: 2));
        setState(() {
          done = true;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        print('is built');
        return done ? Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'You have pushed the button this many times:',
                ),
                Text(
                  '',
                  style: Theme.of(context).textTheme.headline4,
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {},
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        ) : Scaffold(body: CircularProgressIndicator(),);
      }
    }
    

    In which cases would a FutureBuilder work for me instead of the above set up, given the fact that I would also want to optimize my app for less backend reads (in FutureBuilder I would read more than once). I am looking for a case where FutureBuilder would be more usefull and correct than the above setup.

  • user14624595
    user14624595 over 2 years
    This doesn't really answer my question. I am looking for a case where I would need to use FutureBuilder instead of the setup above.
  • Aman khan Roohaani
    Aman khan Roohaani over 2 years
    @user14624595 please update your question with a proper context. make the question more clear.
  • user14624595
    user14624595 about 2 years
    Sure there is impact , there are more rebuilds and api calls into the backend. If for example we used firestore , then we make unnecessary reads, or give more traffic to our backend for no reason. If for example user resizes his window on flutter web, build will be called again without a need, since we already have the data with init state method.
  • user14624595
    user14624595 about 2 years
    so basically if you want to write the best possible code in terms of performance and api calls, futurebuilder is useless, or more like a bad practice. I cant think of a case where the above setup would not work and I would need to use FutureBuilder instead.
  • Mohammed Alfateh
    Mohammed Alfateh about 2 years
    Yes, the out-of-the-box solution can come with downsides.
  • user14624595
    user14624595 about 2 years
    I picked your answer as a correct one, since it was the most helpfull
  • user14624595
    user14624595 about 2 years
    You can refactor the widget where you want the future data, into a different statefull widget and implement initState method there, that way you also rebuild a part of the tree, so no difference there.
  • Arul
    Arul about 2 years
    Stateful widget is heavy for update single child and when the application becomes larger we should avoid use much Stateful Widget and consume Future or GetX or any other method.
  • user14624595
    user14624595 about 2 years
    don't those approaches also call set state under the hood? Do you have a link to what you claim?
  • Arul
    Arul about 2 years
    Of course but it does not affect the full widget tree, its updates a particular widget. And in the case of use FutureBuilder, we shouldn't use setState instead we return the future to the futurebuilder it takes care of the update tree.
  • user14624595
    user14624595 about 2 years
    yes, but since it is in the build function it can be called any number of times, while initState is called only once when the page/widget starts, so only the part of the particular widget will change and not rebuild the entire widget tree