Flutter pages 6 to 10 copied and implemented a Flutter app that uses Firebase authorisation and the cloud database (store and retrieve data on Firebase).
Here, I will look into the tutorial example code in greater detail. The entire listing for this example can be downloaded from github. I modified the code in the step_2 folder following the tutorial from beginning to end1.
Platform
The current platform is defined in package:flutter/foundation.dart
. At the beginning of the main() function, before we do anything else, you can
print(defaultTargetPlatform);
which, for an Android emulator, will be TargetPlatform.android
.
State Management
The whole app is contained in a single ChangeNotifierProvider
, which creates a ChangeNotifier
through the widget ApplicationState
.
ChangeNotifier is
a simple Flutter SDK class which provides change notification to its listeners. In other words, if something is a ChangeNotifier
, you can subscribe to its changes. You can write your own widgets extending ChangeNotifier
.
class MyWidget extends ChangeNotifier{
//
//
void modify(some feature){
// modify some widget feature that requires UI change
notifyListeners();
}
//
}
which calls notifyListeners
() when there is a change.
ChangeNotifier
is part of flutter:foundation
and doesn’t depend on any higher-level classes in Flutter. The gtk_flutter app uses ChangeNotifierProvider
, for which we need the Provider
package, which is included by a line in the pubspec.yaml file:
provider: ^6.0.3
ChangeNotifierProvider
provides an instance of a ChangeNotifier
to its descendants. It rebuilds dependents when `ChangeNotifier.notifyListeners
` is called.
Lay-out
In this one, the lay-out tree goes like:
MaterialApp
Scaffold
AppBar
ListView
Picture
SizedBox (used as separating space)
IconAndDetail (local widget showing date)
IconAndDetail(local widget showing location)
Consumer (for logging in etc)
Divider (a dividing line)
Header
Paragraph (with some text)
Consumer (for the messages)
This is a simple lay-out. Flutter documentation is great in explaining the most important widgets. All of the above are included there.
Authentication
The app execution starts with the following sequence:
main()
runApp()
ChangeNotifierProvider()
In ChangeNotifierProvider
(), we have two important lines:
create: (context) => ApplicationState(),
builder: (context, _) => const App(),
The second line invokes the App. The App is similar to other Flutter Apps with its lay-out summarised in the Lay-out section above2.
The first line creates a ChangeNotifier
instance
for the ApplicationState
. This instance is generated by calling the init
() function of the ApplicationState
class.
Connect to Firebase
The first action of the init() function is to connect to Firebase. This is done by calling Firebase.initializeApp
:
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
The Firebase documentation says that the Firebase.initializeApp
(…) creates and initializes a Firebase app instance. The data in the DefaultFirebaseOptions class of firebase_options.dart file is sent to Firebase during intialisation3.
Check log in
After the initialisation, the following statement is executed. This is a long sequence of lines but corresponds to one single anonymous function call:
FirebaseAuth.instance.userChanges().listen((user) {
print('******* FireBaseAuth user = ${user?.displayName}');
if (user != null) {
_loginState = ApplicationLoginState.loggedIn;
// ... Some lines here reading the messages from this user
notifyListeners();
});
} else {
_loginState = ApplicationLoginState.loggedOut;
_guestBookMessages = [];
_guestBookSubscription?.cancel();
// to here.
}
notifyListeners();
});
}
The userChanges()
method of FirebaseAuth.instance
is a Stream that provides events on all user changes, such as when credentials are linked, unlinked and when updates to the user profile are made. The persistence is automatic and non-optional for Android and iOS as it is spelled out here:
The Firebase SDKs for all platforms provide out of the box support for ensuring that your user's authentication state is persisted across app restarts or page reloads.
On native platforms such as Android & iOS, this behavior is not configurable and the user's authentication state will be persisted on device between app restarts. The user can clear the apps cached data using the device settings, which will wipe any existing state being stored. (Firebase Flutter documentation)
What this means is that when you are logged in, you stay logged in even if you turn the phone off and on again. The information is stored somewhere on the phone by the Firebase SDK functions. This is beyond our control and we cannot change it in our code4.
After ChangeNotifierProvider is initialised in the line
create: (context) => ApplicationState(),
the next line (builder
line) takes over and our App starts generating the screen display using the widget MaterialApp
.
MaterialApp
sets the home
as the HomePage
() widget. As explained in Flutter documentation,
is the route that is displayed first when the application is started normally, unless initialRoute is specified. It's also the route that's displayed if the initialRoute can't be displayed.
…
If home is specified, then routes must not include an entry for
/
, as home takes its place.The Navigator is only built if routes are provided (either via home, routes, onGenerateRoute, or onUnknownRoute); if they are not, builder must not be null.
In this app there is no initialRoute and the App starts with
home: const HomePage(),
After the title and the picture, the appropriate login action button (RSVP, LOG IN, or LOG OUT) is drawn by the Consumer widget. The Consumer widget is an anonymous function that calls Authentication()
with appState
as the argument. Since this is done by a Consumer:builder
line, the second argument is the state of the ChangeNotifierProvider
, which was already initialised as I explain above. We do not have to specify which ChangeNotifierProvider is meant because Flutter allows one and only one ChangeNotifierProvider. If you need more than one, then it is recommended to use MultiProvider.
I did not do the bonus step.
It is also possible to use child
instead of builder.
I am not sure about the difference between the two. Some people posted that you need to use builder
if you pass on the BuildContext
but I do not think this is correct you can also pass the context through the child’s builder parameter if you need to as in
child: Consumer<MainModel>(
builder: (context, model, child) => Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
It is possible that the context in the above code segment is Context
but not the BuildContext
. The difference between these two Flutter context classes is not obvious to me.
I will stick to using builder
as in the tutorial example I am following in this blog page..
This firebase_options.dart file is created with the
$ flutterfire configure
command you enter on the terminal while creating this project in VS Code.
I do not know if the phone user can change it. I do not think so.