Firebase Authentication using Provider in Flutter
Things that you may likely learn from the Blog :
- How to implement authentication into your flutter app?
- How to use the provider package for state management?
- How to use git_it package to inject dependencies?
- How to implement routes for navigation?
Introduction To Provider
Provider is not a state management, it is just a state management helper. Provider package provides us various widgets that help us to consume various changes in values and then rebuild the widget when any changes occur. The most frequently used classes of provider package are Consumer
, ChangeNotifier
, ChangeNotifierProvider
. The model class that is made available to the app widget tree, extends the ChangeNotifier
. This class contains all the app state managing methods and variables and objects. Whenever we update the state of the app we need to call notifiyListeners()
to notify all the widgets who are listening to that particular change so that the UI can be rebuilt and updated. ChangeNotifierProvider
is wrapped at the top of the app widget so that all the widget of the app widget tree can listen to the change made. There are two methods that can be used to consume the updated data. Using Provider.of()
and Consumer
widget.
Install the bellow packages inside your pubspec.yaml file
provider | Flutter Package
English | Português A wrapper around InheritedWidget to make them easier to use and more reusable. By using provider…pub.dev
get_it | Dart Package
This is a simple Service Locator for Dart and Flutter projects with some additional goodies highly inspired by Splat…pub.dev
firebase_auth | Flutter Package
A Flutter plugin to use the Firebase Authentication API. To learn more about Firebase Auth, please visit the Firebase…pub.dev
firebase_core | Flutter Package
A Flutter plugin to use the Firebase Core API, which enables connecting to multiple Firebase apps. To learn more about…pub.dev
Let’s learn how to use routes for navigation
MaterialApp provides us onGenerateRoute
, initialRoute
functionality to implement navigation using routes. onGenerateRoute
takes RouteSetting as a parameter and returns a Route.
Let’s create a separate class to initialize all the navigation cases using the switch method. We will make a static Route<dynamic>
type method that takes RouteSettings
parameters.
routers.dart file
import 'package:flutter/material.dart';
import 'package:flutter_chat_app/ui/homePage.dart';
import 'package:flutter_chat_app/ui/landingPage.dart';
class Routers {
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case 'home':
return MaterialPageRoute(builder: (_) => HomePage());
case 'landing':
return MaterialPageRoute(builder: (_) => LandingPage());
default:
return MaterialPageRoute(builder: (_) {
return Scaffold(
body: Center(
child: Text('No route defined for ${settings.name}'),
),
);
});
}
}
}
Now we can provide the onGenerateRoute
property also initialize the initalRoute
.
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: 'landing',
onGenerateRoute: Routers.generateRoute,
);
}
}
For navigation, we can use pushNamed
method to navigate to another screen using the name of the route name defined in the router.dart
.
onPressed: () async {
Navigator.pushNamed(context, 'route_name');
}
Let’s code
To manage the state of the app we will use enums ViewState
, AuthState
.
enum ViewState { Ideal, Busy }
enum AuthState { SignIn, SignUp }
Let’s build a BaseModel
class that extends ChangeNotifier
. This class will contain all the methods that we will use the update the UI.
import 'package:flutter/material.dart';
import 'package:flutter_chat_app/enum/appState.dart';
class BaseModel extends ChangeNotifier {
ViewState _viewState;
ViewState get viewState => _viewState;
setViewState(ViewState viewState) {
_viewState = viewState;
notifyListeners();
}
AuthState _authState;
AuthState get authState => _authState;
setAuthState(AuthState authState) {
_authState = authState;
notifyListeners();
}
}
Here we have created two methods to change the state. setViewState
method is used to switch the ViewState
from Idel
to Busy
and vice-versa. setAuthState
method is used to switch theAuthState
from SignIn
to SignUp
and vice-versa.
Now create a AuthModel
class that extends BaseModel
. This class will contain all the auth methods.
authModel.dart file
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_chat_app/enum/appState.dart';
import 'baseModel.dart';
class AuthModel extends BaseModel {
FirebaseAuth firebaseAuth = FirebaseAuth.instance;
void createNewUser(String email, String password) async {
setViewState(ViewState.Busy);
await firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
setViewState(ViewState.Ideal);
}
void signIn(String email, String password) async {
setViewState(ViewState.Busy);
await firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
setViewState(ViewState.Ideal);
}
void logOut() async {
setViewState(ViewState.Busy);
await firebaseAuth.signOut();
setViewState(ViewState.Ideal);
}
}
The above class will be used to provide the various methods that will handle the authentication of our app. Now we will make all the methods that will be used to handle all the UI changes when the user will switch between the sign-in and sign-up option.
authStateModel.dart file
import 'package:flutter/cupertino.dart';
import 'package:flutter_chat_app/enum/appState.dart';
import 'package:flutter_chat_app/model/authModel.dart';
import 'package:flutter_chat_app/model/baseModel.dart';
class AuthStateModel extends BaseModel {
switchAuthenticationState(AuthModel authModel) {
authModel.authState == AuthState.SignIn
? authModel.setAuthState(AuthState.SignUp)
: authModel.setAuthState(AuthState.SignIn);
}
switchAuthenticationMethod(
AuthModel authModel,
TextEditingController emailController,
TextEditingController passwordController,
) {
authModel.authState == AuthState.SignIn
? authModel.signIn(
emailController.text,
passwordController.text,
)
: authModel.createNewUser(
emailController.text,
passwordController.text,
);
}
switchAuthenticationText(AuthModel authModel) {
return authModel.authState == AuthState.SignIn ? "Sign In" : "Sign Up";
}
switchAuthenticationOption(AuthModel authModel) {
return authModel.authState == AuthState.SignIn
? "Create account ??"
: "Already registered ??";
}
}
Till here we have made all the required methods that will be used to update the UI of different screens. Now we will create a BaseView
class that is used to update the UI, pass the model data that we have made earlier.
BaseView takes a builder function that takes BuildContext, Model, and a Widget as a parameter.
BaseView widget can only be used for the model class that extends BaseModel.
baseView.dart file
import 'package:flutter/material.dart';
import 'package:flutter_chat_app/locator.dart';
import 'package:flutter_chat_app/model/baseModel.dart';
import 'package:provider/provider.dart';
class BaseView<T extends BaseModel> extends StatefulWidget {
final Widget Function(BuildContext context, T model, Widget child) builder;
BaseView({
@required this.builder,
});
@override
_BaseViewState<T> createState() => _BaseViewState<T>();
}
class _BaseViewState<T extends BaseModel> extends State<BaseView<T>> {
T model = locator<T>();
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>.value(
//builder: (context) => model,
child: Consumer<T>(builder: widget.builder),
//notifier: model,
value: model,
);
}
}
Let’s build the app UI
landingPage.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_chat_app/model/authModel.dart';
import 'package:flutter_chat_app/ui/authPage.dart';
import 'package:flutter_chat_app/ui/baseView.dart';
import 'package:flutter_chat_app/ui/homePage.dart';
// ignore: must_be_immutable
class LandingPage extends StatelessWidget {
TextEditingController emailController = TextEditingController();
TextEditingController passwordController = TextEditingController();
@override
Widget build(BuildContext context) {
return BaseView<AuthModel>(
builder: (context, authModel, child) => StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
return snapshot.hasData
? HomePage()
: AuthPage(
emailController: emailController,
passwordController: passwordController,
authModel: authModel,
);
}),
);
}
}
authPage.dart file
import 'package:flutter/material.dart';
import 'package:flutter_chat_app/enum/appState.dart';
import 'package:flutter_chat_app/model/authModel.dart';
import 'package:flutter_chat_app/model/authStateModel.dart';
import 'package:flutter_chat_app/ui/baseView.dart';
class AuthPage extends StatelessWidget {
final TextEditingController emailController;
final TextEditingController passwordController;
final AuthModel authModel;
AuthPage({
@required this.emailController,
@required this.passwordController,
@required this.authModel,
});
@override
Widget build(BuildContext context) {
return BaseView<AuthStateModel>(builder: (context, authStateModel, __) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20),
child: Center(
child: Column(
children: [
SizedBox(
height: 200,
),
TextFormField(
decoration: InputDecoration(hintText: "Email"),
controller: emailController,
),
TextFormField(
controller: passwordController,
decoration: InputDecoration(hintText: "Password"),
),
authModel.viewState == ViewState.Busy
? CircularProgressIndicator()
: RaisedButton(
child: Text(
authStateModel.switchAuthenticationText(authModel)),
color: Colors.cyanAccent,
onPressed: () {
authStateModel.switchAuthenticationMethod(
authModel, emailController, passwordController);
}),
InkWell(
onTap: () {
authStateModel.switchAuthenticationState(authModel);
},
child:
Text(authStateModel.switchAuthenticationOption(authModel)),
),
],
),
),
),
);
});
}
}
homePage.dart file
import 'package:flutter/material.dart';
import 'package:flutter_chat_app/model/authModel.dart';
import 'package:flutter_chat_app/ui/baseView.dart';
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BaseView<AuthModel>(
builder: (context, model, __) => Scaffold(
body: Center(
child: RaisedButton(
color: Colors.cyanAccent,
child: Text("Log Out"),
onPressed: () {
model.logOut();
},
),
),
),
);
}
}
Register all the services with the locator
locator.dart file
import 'package:flutter_chat_app/model/authModel.dart';
import 'package:flutter_chat_app/model/authStateModel.dart';
import 'package:flutter_chat_app/model/baseModel.dart';
import 'package:get_it/get_it.dart';
final locator = GetIt.instance;
void setupLocator(){
locator.registerLazySingleton(() => BaseModel());
locator.registerLazySingleton(() => AuthModel());
locator.registerLazySingleton(() => AuthStateModel());
}
Initialize the setupLocator() method inside your main function
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
setupLocator();
runApp(MyApp());
}
GitHub Link
flutter-devs/flutter_auth_provider
A new Flutter application. This project is a starting point for a Flutter application. A few resources to get you…github.com
Thanks for reading this article ❤
If I got something wrong? Let me know in the comments. I would love to improve.
Clap 👏 If this article helps you.
If we got something wrong? Let me know in the comments. we would love to improve.
FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire a flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! You can connect with us on Facebook, GitHub, Twitter, and LinkedIn for any flutter related queries.
We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences!.