DEV Community

Alexandre for SNOWCODES

Posted on β€’ Edited on

4 1

[Flutter] Firebase authentication : Dynamic routing by AuthStateChanges πŸ”₯

Hi there πŸ‘‹

Today, dynamic routing with Firebase by AuthStateChanges.

What we want ? We want routing on LoginPage or HomePage automatically when user state changed.

If user is connected and suddenly token expired (or is disconnected), we want redirect to LoginPage.

Initialize app

First of all, we starting by initialized Firebase app.
(In this example, no catch, but think to catch errors).

import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // TODO: Handle error for Firebase.initializeApp
  await Firebase.initializeApp();

  runApp(MyApp());
}
Enter fullscreen mode Exit fullscreen mode

Define our pages and routes

Create two pages HomePage and LoginPage. And add it to onGenerateRoute of the MyApp widget.

UnknownPage for all others pages. 😎

1. HomePage

This page will disconnect our user.
Think to add a simple loader for example while requesting.

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: ListView(
      padding: EdgeInsets.all(32),
      children: [
        ElevatedButton(
          onPressed: () {
            FirebaseAuth.instance.signOut();
          },
          child: Text('Sign out'),
        )
      ],
    ));
  }
}
Enter fullscreen mode Exit fullscreen mode

2. LoginPage

This page will connect our user. We'll use signInAnonymously for the example. But use what you need ! πŸ‘Œ

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: ListView(
      padding: EdgeInsets.all(32),
      children: [
        ElevatedButton(
          onPressed: () {
            FirebaseAuth.instance.signInAnonymously();
          },
          child: Text('Sign In'),
        )
      ],
    ));
  }
}
Enter fullscreen mode Exit fullscreen mode

3. UnknownPage

Just a basic red Container

class UnknownPage extends StatefulWidget {
  const UnknownPage({Key? key}) : super(key: key);

  @override
  _UnknownPageState createState() => _UnknownPageState();
}

class _UnknownPageState extends State<UnknownPage> {
  @override
  Widget build(BuildContext context) {
    return Container(color: Colors.red);
  }
}
Enter fullscreen mode Exit fullscreen mode

4. MyApp

Add a stateful widget MyApp with onGenerateRoute.

Handle with a switch the settings.name route.

And return MaterialPageRoute with all right pages.

Add also initialRoute.

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'HelloWorld',
      // ----- Check if user exists => First page app
      initialRoute:
          FirebaseAuth.instance.currentUser == null ? 'login' : 'home',
      onGenerateRoute: (settings) {
        switch (settings.name) {
          case 'home':
            return MaterialPageRoute(
              settings: settings,
              builder: (_) => HomePage(),
            );
          case 'login':
            return MaterialPageRoute(
              settings: settings,
              builder: (_) => LoginPage(),
            );
          default:
            return MaterialPageRoute(
              settings: settings,
              builder: (_) => UnknownPage(),
            );
        }
      },
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Time to be reactive

1. GlobalKey

final _navigatorKey = GlobalKey<NavigatorState>();
Enter fullscreen mode Exit fullscreen mode

This is the key, πŸ”‘

All we want, all we need, is this one.

With this GlobalKey, we have the control to navigate in our app.

class _MyAppState extends State<MyApp> {
final _navigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'HelloWorld',
      navigatorKey: _navigatorKey, // <===== HERE
      onGenerateRoute: (settings) {
      // ...
Enter fullscreen mode Exit fullscreen mode

2. InitState

Add the initState function to listen FirebaseAuth changes.

  late StreamSubscription<User?> _sub;

  @override
  void initState() {
    super.initState();
    _sub = FirebaseAuth.instance.authStateChanges().listen((event) {
      _navigatorKey.currentState!.pushReplacementNamed(
        event != null ? 'home' : 'login',
      );
    });
  }
Enter fullscreen mode Exit fullscreen mode

3. Dispose

⛔️ Don't forget to dispose your last subscription.

  @override
  void dispose() {
    _sub.cancel();
    super.dispose();
  }
Enter fullscreen mode Exit fullscreen mode

Done βœ…

Now all code available here :

import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late StreamSubscription<User?> _sub;
  final _navigatorKey = new GlobalKey<NavigatorState>();

  @override
  void initState() {
    super.initState();

    _sub = FirebaseAuth.instance.userChanges().listen((event) {
      _navigatorKey.currentState!.pushReplacementNamed(
        event != null ? 'home' : 'login',
      );
    });
  }

  @override
  void dispose() {
    _sub.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'HelloWorld',
      navigatorKey: _navigatorKey,
      initialRoute:
          FirebaseAuth.instance.currentUser == null ? 'login' : 'home',
      onGenerateRoute: (settings) {
        switch (settings.name) {
          case 'home':
            return MaterialPageRoute(
              settings: settings,
              builder: (_) => HomePage(),
            );
          case 'login':
            return MaterialPageRoute(
              settings: settings,
              builder: (_) => LoginPage(),
            );
          default:
            return MaterialPageRoute(
              settings: settings,
              builder: (_) => UnknownPage(),
            );
        }
      },
    );
  }
}

class UnknownPage extends StatefulWidget {
  const UnknownPage({Key? key}) : super(key: key);

  @override
  _UnknownPageState createState() => _UnknownPageState();
}

class _UnknownPageState extends State<UnknownPage> {
  @override
  Widget build(BuildContext context) {
    return Container(color: Colors.red);
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: ListView(
      padding: EdgeInsets.all(32),
      children: [
        ElevatedButton(
          onPressed: () {
            FirebaseAuth.instance.signOut();
          },
          child: Text('Sign Out'),
        )
      ],
    ));
  }
}

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: ListView(
      padding: EdgeInsets.all(32),
      children: [
        ElevatedButton(
          onPressed: () {
            FirebaseAuth.instance.signInAnonymously();
          },
          child: Text('Sign In'),
        )
      ],
    ));
  }
}
Enter fullscreen mode Exit fullscreen mode

One more thing

There is many level of details for auth changes.

  /// Notifies about changes to the user's sign-in state (such as sign-in or
  /// sign-out).
  Stream<User?> authStateChanges() =>
      _pipeStreamChanges(_delegate.authStateChanges());

  /// Notifies about changes to the user's sign-in state (such as sign-in or
  /// sign-out) and also token refresh events.
  Stream<User?> idTokenChanges() =>
      _pipeStreamChanges(_delegate.idTokenChanges());

  /// This is a superset of both [authStateChanges] and [idTokenChanges]. It
  /// provides events on all user changes, such as when credentials are linked,
  /// unlinked and when updates to the user profile are made. The purpose of
  /// this Stream is for listening to realtime updates to the user state
  /// (signed-in, signed-out, different user & token refresh) without
  /// manually having to call [reload] and then rehydrating changes to your
  /// application.
Stream<User?> userChanges() => _pipeStreamChanges(_delegate.userChanges());
Enter fullscreen mode Exit fullscreen mode

See you soon 🐼

Quadratic AI

Quadratic AI – The Spreadsheet with AI, Code, and Connections

  • AI-Powered Insights: Ask questions in plain English and get instant visualizations
  • Multi-Language Support: Seamlessly switch between Python, SQL, and JavaScript in one workspace
  • Zero Setup Required: Connect to databases or drag-and-drop files straight from your browser
  • Live Collaboration: Work together in real-time, no matter where your team is located
  • Beyond Formulas: Tackle complex analysis that traditional spreadsheets can't handle

Get started for free.

Watch The Demo πŸ“Šβœ¨

Top comments (1)

Collapse
 
wongalan2002 profile image
wongalan2002 β€’

Thank you

PulumiUP 2025 image

PulumiUP 2025: Cloud Innovation Starts Here

Get inspired by experts at PulumiUP. Discover the latest in platform engineering, IaC, and DevOps. Keynote, demos, panel, and Q&A with Pulumi engineers.

Register Now

πŸ‘‹ Kindness is contagious

Dive into this thoughtful article, cherished within the supportive DEV Community. Coders of every background are encouraged to share and grow our collective expertise.

A genuine "thank you" can brighten someone’s dayβ€”drop your appreciation in the comments below!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found value here? A quick thank you to the author makes a big difference.

Okay