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.
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
// 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(
...
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.
// 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();
},
),
);
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.