5.0 Introduction

Authentication is the cornerstone of any production app, and thanks to Firebase, it is really easy to add to our app.

Feel free to customise the code below as much as you want, but these prebuilt components will be a good starting point to adding authentication to your project.

5.1 main.dart

Flutter Streams are a powerful feature in Dart (the language Flutter is built on) for handling asynchronous data.

Streams allow you to listen to a sequence of data events over time, enabling real-time data updates within your Flutter app.

This is particularly useful for tasks like handling user input, receiving data from a server, or reacting to changes in an app's state (in this case authentication).

You can subscribe to a stream and react to events with listeners which is what we will be doing in our main.dart file

  1. First things first, we want to define a stream so our app can listen to changes in the authentication state.
// main.dart

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
	@override
	Widget build(BuildContext context) {
	  // Add this line to the top of the build function 
	  Stream<User?> authStream = FirebaseAuth.instance.authStateChanges();
	
		return MaterialApp(
		...
  1. We want to build a StreamBuilder around at the root of our app so we pick up auth changes at the very top of our widget tree.

    <aside> 💡 The widget tree is a structure that represents how the widgets are combined and composed together to make larger widgets.

    It’s important to pick up Auth changes at the top of the widget tree so that permission changes can trickle down into lower level widgets.

    </aside>

    Data is accessed in streams via snapshots. In the code below, we can listen to the snapshot and tell our app to do different things based on what the snapshot is telling us.

    1. If we are stilling watching for the snapshot to give us data, we can show the user a loading screen
    2. If the snapshot data is null, we know that there is no signed in User so we can redirect to the Login Page.
    3. Otherwise if there is snapshot data, we know that there is a user logged in and can direct them to a page to start using the app.
// main.dart

return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),

      home: StreamBuilder<User?>(
        stream: authStream,
        initialData: null,
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }

          if (snapshot.data == null) {
            return LoginPage();
          }
          return FeedPage();
        },
      ),
      
    );

5.2 Adding Pages

At this point, your main.dart file should have some errors relating to our LoginPage and FeedPage. Well this is because we haven’t created them yet! Lets do that now.

It’s good practice to keep our lib folder organised, so lets create a pages folder under lib which is where we can put all our new pages.

Untitled