Search code examples
dartflutterfuturebuilder

When should I use a FutureBuilder?


I was wondering when I should use the future builder. For example, if I want to make an http request and show the results in a list view, as soon as you open the view, should I have to use the future builder or just build a ListViewBuilder like:

new ListView.builder(
        itemCount: _features.length,
        itemBuilder: (BuildContext context, int position) {
...stuff here...
}

Moreover, if I don't want to build a list view but some more complex stuff like circular charts, should I have to use the future builder?

Hope it's clear enough!


Solution

  • FutureBuilder removes boilerplate code.

    Let's say you want to fetch some data from the backend on page launch and show a loader until data comes.

    Tasks for ListBuilder:

    • Have two state variables, dataFromBackend and isLoadingFlag
    • On launch, set isLoadingFlag = true, and based on this, show loader.
    • Once data arrives, set data with what you get from backend and set isLoadingFlag = false (inside setState obviously)
    • We need to have a if-else in widget creation. If isLoadingFlag is true, show the loader else show the data. On failure, show error message.

    Tasks for FutureBuilder:

    • Give the async task in future of Future Builder
    • Based on connectionState, show message (loading, active(streams), done)
    • Based on data(snapshot.hasError), show view

    Pros of FutureBuilder

    • Does not use the two state variables and setState
    • Reactive programming (FutureBuilder will take care of updating the view on data arrival)

    Example:

    FutureBuilder<String>(
        future: _fetchNetworkCall, // async work
        builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
           switch (snapshot.connectionState) {
             case ConnectionState.waiting: return Text('Loading....');
             default:
               if (snapshot.hasError)
                  return Text('Error: ${snapshot.error}');
               else
              return Text('Result: ${snapshot.data}');
            }
          },
        )
    

    Performance impact:

    I just looked into the FutureBuilder code to understand the performance impact of using this.

    • FutureBuilder is just a StatefulWidget whose state variable is _snapshot
    • Initial state is _snapshot = AsyncSnapshot<T>.withData(ConnectionState.none, widget.initialData);
    • It is subscribing to future which we send via the constructor and update the state based on that.

    Example:

    widget.future.then<void>((T data) {
        if (_activeCallbackIdentity == callbackIdentity) {
          setState(() {
            _snapshot = AsyncSnapshot<T>.withData(ConnectionState.done, data);
          });
        }
    }, onError: (Object error) {
      if (_activeCallbackIdentity == callbackIdentity) {
        setState(() {
          _snapshot = AsyncSnapshot<T>.withError(ConnectionState.done, error);
        });
      }
    });
    

    So the FutureBuilder is a wrapper/boilerplate of what we do typically, hence there should not be any performance impact.