skip to Main Content

I have a Flutter architecture question on the best approach on how to navigate when we depend on the app state.

Let us assume we have a simple app that

  1. shows login page if user is not logged in. On login the app does more things like track analytics, fetch extra data then needs to show the homepage
  2. shows home page if the user is logged in

In order to know what to show initially, the app checks the secure storage for a previous token/session.

I will list below the two approaches I am aware of and the pros/cons I think they have.

1 approach listening to bloc state

The app can use a blocprovider and listen to the emited states.

Simple pseudocode example

 if (state instanceof LoggedIn) {
      return HomePage();
 }

 return LoginPage();

Pros:

Can be simple to understand

Cons:

If we need to prefetch more data after log-in somehow I see this approach needing to mix business login into the App.dart the presentation layer

2 approach navigating from the bloc

In this approach we can emit one event from the root app like this

  @override
  void initState() {
    super.initState();
    SchedulerBinding.instance.addPostFrameCallback((_) {
      _appBloc.add(AppOpened());
    });
  }

Then on AppOpened event the bloc checks session if the user is already logged in, and based on this information redirects to login or home page using a navigatorKey navigatorKey: _navigationBloc.navigatorKey,

pros:

  1. can do more extensive login in general like prefetch data, images etc. Even after the login-in, can make many api calls in parallel to fetch data, track analytics then when done just navigate to the desired page

cons:

The bloc pattern will be abused and the bloc will be also doing navigation not just receiving data and reducing a new state

Question

What do you think is the best approach ?
Please feel free to suggest other approaches as well. Those 2 are the only ones I am aware at the moment.

2

Answers


  1. You can use a FutureBuilder like:

    FutureBuilder(
          future: _appState.checkForLoginCredentials(),
          builder: (BuildContext context, AsyncSnapshot<List<bool>> snapshot) {
            return (snapshot.connectionState == ConnectionState.done)
                ? _getLauncherScreen(snapshot.data)
                : Container();
          },
        ))
    

    which would call a method returning a boolean array like

        Future<List<bool>> checkForLoginCredentials() async {
        String? authToken = await getAuthToken();
        bool? onboarded = await isOnBoarded();
        ProfileResponse? profile = await getUserProfile();
    
        var isLoggedIn = authToken != null && authToken.isNotEmpty;
        var isProfileComplete = profile != null;
    
        return [
          onboarded ?? false,
          isLoggedIn,
          isProfileComplete
        ];
      }
    

    You can use the values of the array of navigate to different parts of the application like:

    //Returns the Starting Screen of Application based on the application journey
      //of the User.
      Widget _getLauncherScreen(List<bool>? data) {
        if (data == null) return Container();
    
    
        final isOnBoarded = data[0];
        final isLoggedIn = data[1];
        final isProfileComplete = data[2];
    
        if (!isOnBoarded && !kIsWeb) {
          return const OnBoardingScreen();
        } else if(!isLoggedIn){
          return const LoginBaseScreen();
        } else if(!isProfileComplete) {
          return const CreateProfileBaseScreen();
        } else {
          return const ParentBaseScreen();
        }
      }
    
    Login or Signup to reply.
  2. There are many ways to handle redirects in Flutter, but the one I use the most is with the go_router package along with Provider or any other state management solution you prefer, including listeners. The idea is to use the redirect function to manage all redirects, even if the user is logged in and you suspend their session; it will automatically return them to the login screen.

    You can find more information about the go_router package here.

    GoRouter(
        refreshListenable: notifierRouter,
        initialLocation: '/',
        debugLogDiagnostics: true,
        routes: routeList,
        redirect: redirectRoute,
    );
    

    Here are some validations you could perform, just as an example:

    String? redirectRoute(BuildContext context, GoRouterState state) {
        if (this.state.hasError) return null;
    
        if (state.fullPath == '/' && isLogged && isActive && !isExpiredSession) return '/home';
    
        if (state.fullPath == '/') return null;
    
        if (isLogged && isActive && isExpiredSession) return '/';
    
        if (!isLogged) return '/';
    
        return null;
    }
    

    Remember to adjust the routes and conditions according to the specific needs of your application. I hope this helps!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search