Search code examples
flutterparse-platformriverpod

Retrigger FutureBuilder on Login / Registration


I'm currently stuck at what I hope is a simple problem, I just tried so much that I probably just can't see the solution any longer.

I have a Landing Page that checks via a future whether the user has an active session or not (Parse Backend). I manage to make successful login and registration requests, just the screen doesn't change, meaning the future builder doesn't rebuild. When I hot reload everything works fine, but I don't manage to automatically trigger the hot reload. I user Riverpod for state management. The hasUserLogged() Method is supplied via Riverpod by an AuthBase class. I hand over the updatedUser method to the AuthScreen to trigger it on login/signUp, but it doesn't trigger a rebuild of the FutureBuilder. I thought getting an updatedUser from Server would also supply me in the next step with information whether the user has its email verified, but that's the follow up problem (but I would appreciate a pointer in the right direction how to solve the 4x4 user matrix: has token / no token & verified / unverified e-mail and redirecting to Auth / Verify E-Mail / HomePage depending on combinations..)

Anyhow, for now - how can I trigger the rebuild of the FutureBuilder upon Login/SignUp Button press in the AuthScreen?


class LandingPage2 extends StatefulWidget {
  @override
  _LandingPage2State createState() => _LandingPage2State();
}

class _LandingPage2State extends State<LandingPage2> {

  Future<ParseUser> _updateUser() async {
    final auth = context.read(authProvider);

    ParseUser currentUser = await ParseUser.currentUser() as ParseUser;

    if (currentUser != null) {
      ParseResponse update = await currentUser.getUpdatedUser();
      if (update.success) {
        currentUser = update.result as ParseUser;
        await auth.hasUserLogged();
        setState(() {
          return currentUser;
        });
      }
    }

    if (currentUser == null) {
      print('null User');
    }
  }
  

  
   /// Check if user session token is valid
   Future<bool> hasUserLogged() async {
     ParseUser currentUser = await ParseUser.currentUser() as ParseUser;
     // return false if no user is logged in
     if (currentUser == null) {
       return false;
     }
     //Validates that the user's session token is valid
     final ParseResponse parseResponse =
         await ParseUser.getCurrentUserFromServer(
             currentUser.get<String>('sessionToken'));
  
     if (!parseResponse.success) {
       print('invalid session. logout');
       //Invalid session. Logout
       await currentUser.logout();
       return false;
     } else {
       print('login successfull');
       return true;
     }
   }

  @override
  Widget build(BuildContext context) {
    final auth = context.read(authProvider);

    return FutureBuilder<bool>(
      future: auth.hasUserLogged(),
      builder: (context, snapshot) {
        print('futurebuilder rebuild');
        switch (snapshot.connectionState) {
          case ConnectionState.none:
           case ConnectionState.waiting:
             return SplashScreen();
             break;
          default:
            if (snapshot.hasData && snapshot.data) {
              return HomePage();
            } else {
              return AuthScreen(_updateUser);
            }
        }
      },
    );
  }
}

Any help is highly appreciated, struggle since hours and my head can't wrap around why it is not working :-/


Solution

  • Thank you @Randal Schwartz, 'watch' made it happen, after I created an AuthNotifier and StateNotifierProvider to manage the user state and depending on that user in the hasUserLogged() method.

    If anyone is also struggling - that's the working version:

    import 'dart:async';
    
    import 'package:app/screens/splash_screen.dart';
    import 'package:app/services/top_level_providers.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
    
    import 'auth_screen.dart';
    import 'home.dart';
    
    class LandingPage2 extends ConsumerWidget {
      @override
      Widget build(BuildContext context, ScopedReader watch) {
        final auth = watch(authProvider);
    
        /// Check if user session token is valid
        Future<bool> hasUserLogged() async {
          print('hasUserLogged - Fired');
    
          /// watch current User state  -- Triggers rebuild after login!
          final authNotifier = watch(authNotifierProvider.state);
          final ParseUser currentUser = authNotifier;
    
          // return false if no user is logged in
          if (currentUser == null) {
            print('currentUserNULL');
            return false;
          }
    
          //Validates that the user's session token is valid
          final ParseResponse parseResponse =
              await ParseUser.getCurrentUserFromServer(
                  currentUser.get<String>('sessionToken'));
    
          if (!parseResponse.success) {
            print('invalid session. logout');
            //Invalid session. Logout
            await currentUser.logout();
            return false;
          } else {
            print('login successfull');
    
            return true;
          }
        }
    
        return FutureBuilder<bool>(
          future: hasUserLogged(),
          builder: (context, snapshot) {
            print('futurebuilder rebuild');
            // print(snapshot.data);
            switch (snapshot.connectionState) {
              case ConnectionState.none:
              case ConnectionState.waiting:
                return SplashScreen();
                break;
              default:
                if (snapshot.hasData && snapshot.data) {
                  /// Add Verify E-Mail Logic here - Another Future Builder??
                  return HomePage();
                } else {
                  // _updateUser();
                  return AuthScreen();
                }
                break;
            }
          },
        );
      }
    }
    
    

    The Auth Notifier:

    /// Auth Notifier Class
    class AuthNotifier extends StateNotifier<ParseUser> {
      AuthNotifier(ParseUser state) : super(state);
    
      setCurrentUser(ParseUser user) {
        state = user;
      }
    
      void clearUser() {
        state = null;
      }
    }
    

    And the provider:

    final authNotifierProvider = StateNotifierProvider((ref) {
      return AuthNotifier(null);
    });
    
    

    This is triggered after the active User after login / registration is received and thus triggers rebuild of hasUserLogged.

    authNotifier.setCurrentUser(user);

    Appreciate the help! Did cost me a lot of time... Having to switch away from firebase sucks...