Search code examples
flutterdartflutter-futurebuilder

How to handle async load of data to display in flutter


what I'm trying to achieve here is a functionality that firstly tries to load the data stored in local storage (if any) and then fetch the new data from the API and displays and stores them in local storage.

I'm thinking of two ways to do that but since I'm not familiar with Flutter, I actually don't know how exactly to do that.

Imagine doing that in React Js/Native:

First Way

  1. use a state hook to store the data
  2. display an activity indicator if state is null/empty
  3. read the data from storage and display them to user
  4. fetch the new data, update the state and store the new data in local storage

Second Way

  1. use a state hook to store the data
  2. display an activity indicator if state is null/empty
  3. read the data from storage and display them to user
  4. re-read the data from storage while listening to a event listener (the event emits from an another file after data gets updated and stored on local storage)

I know a few things about FutureBuilder() in flutter and using it to fetch the data and then display them but I was wondering how to achieve this functionality in Flutter.


Solution

  • To achieve your goal i would use FutureBuilder mixed with some custom logic.

    For example i would use localstorage package to store and retrieve data from localStorage.

    I would create 2 methods.

    • One for fetching your service and update the localStorage
    • The other to retrive the localStorage cache

    Then in you component, fetch the service, but in the meanwhile, show the cached data if present.


    So you could do something like this in your Service class (I will refer to this as OurService in the next snippet)

    import 'package:localstorage/localstorage.dart';
    
    ...
    
    final LocalStorage storage = new LocalStorage('my_app');
    
    Future fetchData() async {
          // Fetch your service
          final response = await callYourService();
          // Set your cache
          storage.setItem('someSpecificDataKey', response)
          return Future.value(response);
    }
    
    SomeType? getLocalData() async {
       final cacheData = storage.getItem('someSpecificDataKey')
       if (cacheData != null) {
          // Return cached data
          return cacheData;
       } return null;
    }
    

    and then in your Widget do something like

    OurService service = OurService(); // this refer to the above snippet
    late SomeType? localData;
    
    @override
    void initState() {
        super.initState();
        // Get the cache value (possibly null)
        localData = service.getLocalData();
    }
    
    
    Widget build(context) {
      return FutureBuilder<dynamic>(
          future: service.fetchData(), // Fetch the service
          builder:  (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.done) {
                // Render the latest fetched value (When the fetch is complete)
              }
              if (localData != null) {
                  // While the fetch is still resolving
                  // here you can render something using `localData`
              }
              // Render something when you have no `localData` and the fetch is still running      
          }
      )
    }
    

    Then if you need to fetch again your service, simply call setState() in this component, will re-fetch the service and will show the last cached value in the meanwhile.

    Calling setState() will create a new Future so the process will start again as in the beginning.

    I think this is may be a good starting point to solve your needs :)