Intro to State Management in Flutter! Using Provider to check if user is already logged in.

in Project HOPE4 years ago

image.png

Flutter and State Management

While working on the Skica app, one issue I noticed is that when closing and reopening an app, the users are asked to login every time since the login screen is the first page they're intended to see.

I started thinking of different solutions. But, the one I ended up settling on was using a "wrapper" that is loaded first. This wrapper checks to see if a user is already logged in. If so, it sends them to the home page. If not, to the login.

Because the Wrapper is the top-level page, we must use the Provider package (Provider is the standard for state management), to pass that information up our widget tree to the Wrapper. I will show you how this was done below.

app.dart

class App extends StatelessWidget {
  static FirebaseAnalytics analytics = FirebaseAnalytics();

  @override
  Widget build(BuildContext context) { 
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<AuthService>(create: (_) => AuthService()),
        StreamProvider<User>.value(
          value: FirebaseAuth.instance.authStateChanges(),
        ),
      ],
      child: MaterialApp(
        home: Wrapper(),
        navigatorObservers: [FirebaseAnalyticsObserver(analytics: analytics)],
        theme: skicaTheme,
      ),
    );
  }
}

This is the entry point to the app. As you can see, MaterialApp has Wrapper returned as it's home option. So when the app is started, it starts by creating a MultiProvider widget(where we declare our different state objects) and that has a MaterialApp child.

Now, wrapper.dart

class Wrapper extends StatelessWidget {
  Wrapper({Key key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    final User user = Provider.of<User>(context);
    final AuthService authService = Provider.of<AuthService>(context);
    if (authService.loading) {
      return Spinner();
    } else if (user == null) {
      return LoginPage();
    } else {
      return HomePage();
    }
  }
}

Our wrapper.dart is pretty simple. If the AuthService is loading, it gives us a loading spinnger. If user, which is equal to a Provider, is null, we get the login page. Otherwise, give us the home page.

Now to see how the state is passed, I will show a snippet of the Google auth in my AuthService class.

auth.dart

class AuthService with ChangeNotifier {
  bool loading = false;

  // Signin with Google.
  Future<void> signInWithGoogle() async {
    // See: https://firebase.flutter.dev/docs/auth/social/
    loading = true;
    notifyListeners();
    final GoogleSignInAccount googleUser = await GoogleSignIn().signIn();
    if (googleUser?.authentication != null) {
      final GoogleSignInAuthentication googleAuth =
          await googleUser.authentication;
      final GoogleAuthCredential credential = GoogleAuthProvider.credential(
        accessToken: googleAuth.accessToken,
        idToken: googleAuth.idToken,
      );
      await FirebaseAuth.instance.signInWithCredential(credential);
    }
    loading = false;
    notifyListeners();
  }

Here you can see that the signInWithGoogle function has a loading boolean set to true at the beginning of the function. This is what is being referenced in the Wrapper. The wrapper is aware of this because of the line below the loading boolean which is notifyListeners().

notifyListeners() is what notifies your Providers of the current state of something. This is why the Wrapper is able to check on the value of authService.loading.

Because this app is integrated with Firestore, we have a nice simple way to handle social logins. If you look back to the app.dart file in our MultiProvider, you can see where the Providers are listening for the changes.

app.dart snippet

providers: [
        ChangeNotifierProvider<AuthService>(create: (_) => AuthService()),
        StreamProvider<User>.value(
          value: FirebaseAuth.instance.authStateChanges(),
        ),
      ],

If there are changes, the state is made available to the widgets descendents. ChangeNotifierProvider and StreamProvider do behave in slightly different ways, but essentially they both make the state of something available.

I know to some this may seem a bit complicated. I know it was one of the harder things for me to grasp at first. But, once you start trying to implement it yourself, it becomes a lot easier to understand.

That's all for my little intro to Flutter and State! Give me a follow if you're interested in programming/software engineering and crypto and blockchain! I will be posting a lot more like this!

Sort:  

There is a community for developers here https://steemit.com/trending/hive-192037

Oh, cool! I was trying to find a good one. But all the ones I came across only had like 40 members. I just joined that one though.

Seems like this group is only about work on Steem related projects though.

 4 years ago 

Hello @nolyoi

Thank you for posting within our community.

Please spare few minutes and read how project.hope is organized and learn about our economy.

That would help you understand more our goals and how are we trying to achieve them. Hopefully you will join our community and become strong part of it :)

Do you use telegram or discord? If you do then join our server and give me a shout. I would gladly share with you goals of our community and introduce to others from our team.

Consider joining our discord server: https://discord.gg/uWMJTaW

Yours,
@project.hope team,