Search code examples
flutterflutter-providerriverpod

How to update provider value on a future builder in Flutter using Riverpod?


I am trying to implement an auto-login feature. Basically on the startup, I want to check if any user data is saved at Flutter Secure Storage and then update my userProvider based on the result.

Here is the build method of the first screen

  @override
  Widget build(BuildContext context, WidgetRef ref) {

    return FutureBuilder(
      future: checkUser(),
      builder: (context, snapshot) {

        if(snapshot.hasData){
          print("has data");
          ref.read(userProvider.notifier).setUser(
            snapshot.data!
          );
        }
        else if(snapshot.hasError){
          print(snapshot.error);
        }
        else{
          
          return Container(
            color: Colors.white,
            child: const Center(
              child: CircularProgressIndicator.adaptive(),
            ),
          );
        }

        return AppBody(context);
      },
    );
  }

And here is the provider I am trying to update

final userProvider = StateNotifierProvider<UserNotifier, User?>((ref) {
  return UserNotifier();
});

It gives me this error:

Tried to modify a provider while the widget tree was building. If you are encountering this error, chances are you tried to modify a provider in a widget life-cycle, such as but not limited to:

  • build
  • initState
  • dispose
  • didUpdateWidget
  • didChangeDepedencies

Where can i update the provider value if I can not do it on the initializing?

Thank you for your time and answers.


Solution

  • You could try using a FutureProvider to load the cached user data and consume the provider in the widget's build method.

    final userProvider = FutureProvider<User?>((ref) async {
      final user = await ... // load user data from flutter secure storage
      return user;
    });
    
    @override
    Widget build(BuildContext context, WidgetRef ref) {
      final AsyncValue<User?> userAsync = ref.watch(userProvider);
      return userAsync.when(
        data: (user) {
          if (user == null) {
            return // login page;
          } else {
            return // home page;
          }
        },
        loading: () => const Text('loading'),
        error: (error, stack) => const Text('error'),
      );
    }
    

    if you want to keep track of the user state, you could define a isLoggedInProvider.

    final isLoggedInProvider = Provider<bool>((ref) {
      final user = ref.watch(userProvider).asData?.value;
      return user != null;
    });