skip to Main Content

I am developing a Flutter app. Upon login I store the User ID in shared preferences. Now I want to show the User Profile in the Navigation Drawer. The User Profile consists of: student’s name, class, div, profile image. The image will not be updated frequently, so I am ruling out the use of a stream from node.js.

If I use a one time API call, how should I store the data? Should I invoke the API every time the user opens the drawer, or is there any other way to handle this? Invoking the API every time will block the screen until the data loads; even if data hasn’t changed, or should I switch to using a stream.

2

Answers


  1. I’d store the whole profile in shared prefs, then everytime (but not more than once every 60 seconds) user opens the drawer there is a call to api in the background to see if anything has changed.

    Most elegant solution would be to use a FutureBuilder for the drawer, and every time drawer is opened and connectionState is active show shared prefs data with a little sliver at the top saying ‘updating…’. For every other ConnectionState just show data from shared prefs, and if there’s no data in shared then show progress indicator.

    With this approach you have a drawer that is responsive every time, shows when is actively fetching data, and does not abuse the API since as you said data is not changing frequently.

    Login or Signup to reply.
  2. Riverpod

    A Reactive Caching and Data-binding Framework

    I would look into something like Riverpod. Although it is additional overhead, and may be intimidating at first, it offers a great solution to such problems.

    You could then utilize a shared provider across multiple widgets, components, consumers. Check out the different types of providers and the use example use case descriptions for them. When watching a provider you can also leverage the AsyncValue type which leads you into handling scenarios such as: loading, error, and data.

    AsyncValue Example

    This is from their documentation and doesn’t show the implementation of the provider. That can be found in the other documentation, as that can be a deeper subject. I would definitely recommend checking out their documentation on the site and looking at the examples there.

    class Example extends ConsumerWidget {
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final AsyncValue<User> user = ref.watch(userProvider);
    
        return user.when(
          loading: () => CircularProgressIndicator(),
          error: (error, stack) => Text('Oops, something unexpected happened'),
          data: (user) => Text('Hello ${user.name}'),
        );
      }
    }
    

    Refreshing Data

    Utilizing Riverpod will help you build "reactive" widgets, which will help handle the flow of data and performantly rebuilding widgets as needed.

    As for refreshing data, I likely wouldn’t create a live stream for it, and instead just refresh it as required. If the user will be the only one to see their own data, then just update the provider state when the user updates their own data, and the consumers will update accordingly as they should be watching the provider. If the user’s data will be displayed to other users, then you must determine the value of trying to stream such to other users — this can also become a scaling problem with large sets of users (an n^2 problem every event for every user has to be replicated across all users). Likely that information is superficial, and if it is outdated it does not ultimately matter. When the clients refresh their views they can see the updated data then.

    Blocking on Refresh

    Riverpod by default will skip the loading state when refreshing data. It can trigger the loading state on the initial load, and then continue to display the old data until the new data comes through. There are parameters on the AsyncValue to override this behavior though if you wanted via skipLoadingOnRefresh that can be set to false. To be honest their documentation doesn’t seem to go over that much and it’s hard/impossible to find much on it and invalidation (different than a refresh).

    user.when(
      loading: () => CircularProgressIndicator(),
      error: (error, stack) => Text('Oops, something unexpected happened'),
      data: (user) => Text('Hello ${user.name}'),
      skipLoadingOnRefresh: false,
    );
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search