Flutterexperts

Empowering Vision with FlutterExperts' Expertise
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 SignInto SignUpand 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!.

Leave comment

Your email address will not be published. Required fields are marked with *.