Friday, October 19, 2018

Flutter – Login and registration authentication with Firebase

Authentication is a process of identifying a user that is usually based on a username and password. A user can access the certain resource in the application after successfully authenticate.

firebase-auth-login-register
Firebase provides a user authentication feature that is out-of-the-box. Firebase is a cloud service provider that can replace whole server-side part of your application. We do not need any server-side code or configuration. Firebase automatically stores your users’ credentials securely. This separates sensitive user credentials from your application data and lets you focus on the user interface and experience for your app. Firebase comes with a bundle of auth features support. As you can see below:


In this post, we'll create a project that includes a sign-up and login process with the help of Firebase Authentication. We'll learn about following authentication feature of Firebase.

1. Email/Password Authentication.
2. Google
Authentication.
3. Phone 
Authentication.

As you can see, we have enabled it.


Before going to user Firebase in the Flutter app. We have to configure Firebase in the Flutter project. I'll recommend you to go through the last post from the below link if you never configure it in a Flutter application.

Firebase integration and configuration in a Flutter Application.

The final output of the project look like below:



Let's start our Flutter project development with the following steps. 

Creating a new Project
1. Create a new project from File ⇒ New Flutter Project with your development IDE.

2. To use Firebase Authentication API's in a Flutter app, We have to use some dependencies as we have declared below. You can use the latest or stable version of the following dependencies from here.
pubspac.yaml
dependencies: flutter: sdk: flutter cupertino_icons: ^0.1.2 http: "^0.11.3+17" firebase_auth: 0.6.2+1 google_sign_in: ^3.2.1 fluttertoast: ^2.0.7 flutter_svg: ^0.6.1+1 dev_dependencies: flutter_test: sdk: flutter flutter: uses-material-design: true assets: - assets/email.svg - assets/gmail.svg - assets/smartphone.svg
in this project, we have used some SVG assets like email, gmail and smartphone as you can see above. By using this, we'll create a user interface for sign-up and login.

3. Open main.dart file and edit it. As we have set our theme and change debug banner property of Application.
main.dart
import 'package:flutter/material.dart'; import 'package:flutter_firebase_auth/view/login/login.dart'; void main() { runApp(new MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( theme: new ThemeData( primaryColor: const Color(0xFF02BB9F), primaryColorDark: const Color(0xFF167F67), accentColor: const Color(0xFFFFFFFF), ), debugShowCheckedModeBanner: false, title: 'Firebase', home: new LoginPage(), ); } }
4. After that create a login.dart file that'll display sign-up and login interface to the user. As you know, we going to use login/password, google, and phone authentication. We have used three bool  _isPhoneAuthEnable, _isGoogleAuthEnable and _isEmailAuthEnable. When we change the state of these variables. It will change user interface for the phone, google and email/password.
login.dart
bool _isPhoneAuthEnable = true; bool _isGoogleAuthEnable = false; bool _isEmailAuthEnable = false; bool _isLoading = false; final _scaffoldKey = new GlobalKey<ScaffoldState>(); // final _teMobileEmail = TextEditingController(); final _teCountryCode = TextEditingController(); final _tePassword = TextEditingController(); FocusNode _focusNodeMobileEmail = new FocusNode(); FocusNode _focusNodeCountryCode = new FocusNode(); FocusNode _focusNodePassword = new FocusNode(); FirebasePhoneUtil firebasePhoneUtil; FirebaseGoogleUtil firebaseGoogleUtil; FirebaseAnonymouslyUtil firebaseAnonymouslyUtil;
in the bottom of above snippet. We have references of some util classes. In these classes, we have defined functionality for getting auth via login/password, phone, and Google. As you can see below, we have created instances of these classes.
login.dart
@override void initState() { super.initState(); firebasePhoneUtil = FirebasePhoneUtil(); firebasePhoneUtil.setScreenListener(this); firebaseGoogleUtil = FirebaseGoogleUtil(); firebaseGoogleUtil.setScreenListener(this); firebaseAnonymouslyUtil = FirebaseAnonymouslyUtil(); firebaseAnonymouslyUtil.setScreenListener(this); }
now edit build method of the login widget as you can see below, we have used ProgressHUD as the root of the login screen. It'll show progress loader. The child of our screen is screenRoot that contains all widgets of the login screen.
login.dart
var screenRoot = new Container( height: double.maxFinite, alignment: FractionalOffset.center, child: new SingleChildScrollView( child: new Center( child: loginForm, ), ), );
return new Scaffold( backgroundColor: const Color(0xFF2B2B2B), appBar: null, key: _scaffoldKey, body: ProgressHUD( child: screenRoot, inAsyncCall: _isLoading, opacity: 0.0, ), );
let's create some widgets that we'll use to create a login screen. like, we have a tab widget. It'll show email/password, Google and phone user interface for sign-up and login.
login.dart
var tabs = new Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new GestureDetector( onTap: () { phoneTabEnable(); }, child: new SvgPicture.asset( "assets/smartphone.svg", height: 30.0, width: 30.0, color: _isPhoneAuthEnable ? new Color(0xFF2CB044) : new Color(0xFF626262), allowDrawingOutsideViewBox: true, ), ), new SizedBox( width: 10.0, ), new GestureDetector( onTap: () { gMailTabEnable(); }, child: new SvgPicture.asset( "assets/gmail.svg", height: 38.0, width: 38.0, color: _isGoogleAuthEnable ? new Color(0xFF2CB044) : new Color(0xFF626262), allowDrawingOutsideViewBox: true, ), ), new SizedBox( width: 10.0, ), new GestureDetector( onTap: () { eMailTabEnable(); }, child: new SvgPicture.asset( "assets/email.svg", height: 30.0, width: 30.0, color: _isEmailAuthEnable ? new Color(0xFF2CB044) : new Color(0xFF626262), allowDrawingOutsideViewBox: true, ), ), ], );
 here, we have a phone authentication user interface widget.
login.dart
var phoneAuthForm = new Column( crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ new Row( children: <Widget>[ new Expanded( child: new TextFormField( controller: _teCountryCode, focusNode: _focusNodeCountryCode, decoration: InputDecoration( labelText: "Code", hintText: "Country code", fillColor: new Color(0xFF2CB044), ), ), flex: 1, ), new SizedBox( width: 10.0, ), new Expanded( child: new TextFormField( controller: _teMobileEmail, focusNode: _focusNodeMobileEmail, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: "Mobile Number", hintText: "Mobile number", fillColor: new Color(0xFF2CB044), prefixIcon: new Icon(Icons.mobile_screen_share), ), ), flex: 5, ), ], ), new SizedBox( height: 10.0, ), tabs ], );
now create a widget for email/password mode.
login.dart
var anonymouslyForm = new Column( crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ new TextFormField( controller: _teMobileEmail, focusNode: _focusNodeMobileEmail, decoration: InputDecoration( labelText: "Please enter email", hintText: "Email", fillColor: new Color(0xFF2CB044), prefixIcon: new Icon(Icons.email), ), ), new SizedBox( height: 10.0, ), new TextFormField( controller: _tePassword, focusNode: _focusNodePassword, decoration: InputDecoration( labelText: "Password", hintText: "Passwrod", fillColor: new Color(0xFF2CB044), prefixIcon: new Icon(Icons.keyboard_hide), ), ), new SizedBox( height: 10.0, ), tabs ], );
in the end, we have created google account widget. We have added all these widgets in the build method of the login screen.

logiin.dart
var googleForm = new Column( crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ new SizedBox( height: 20.0, ), new Center( child: new CircularProgressIndicator( valueColor: new AlwaysStoppedAnimation<Color>(Colors.green), ), ), new SizedBox( height: 40.0, ), tabs ], );




here, you can see a complete file of login widget.

login.dart
import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter_firebase_auth/util/app_util.dart'; import 'package:flutter_firebase_auth/util/firebase_anonymously_util.dart'; import 'package:flutter_firebase_auth/util/firebase_google_util.dart'; import 'package:flutter_firebase_auth/util/firebase_listenter.dart'; import 'package:flutter_firebase_auth/util/firebase_phone_util.dart'; import 'package:flutter_firebase_auth/view/otp_verification/otp_verification_screen.dart'; import 'package:flutter_firebase_auth/view/user_dashboard/user_dashboard.dart'; import 'package:flutter_firebase_auth/widget/progress_hud.dart'; import 'package:flutter_svg/flutter_svg.dart'; class LoginPage extends StatefulWidget { @override _LoginScreenState createState() => new _LoginScreenState(); } class _LoginScreenState extends State<LoginPage> implements FirebaseAuthListener { bool _isPhoneAuthEnable = true; bool _isGoogleAuthEnable = false; bool _isEmailAuthEnable = false; bool _isLoading = false; final _scaffoldKey = new GlobalKey<ScaffoldState>(); final _teMobileEmail = TextEditingController(); final _teCountryCode = TextEditingController(); final _tePassword = TextEditingController(); FocusNode _focusNodeMobileEmail = new FocusNode(); FocusNode _focusNodeCountryCode = new FocusNode(); FocusNode _focusNodePassword = new FocusNode(); FirebasePhoneUtil firebasePhoneUtil; FirebaseGoogleUtil firebaseGoogleUtil; FirebaseAnonymouslyUtil firebaseAnonymouslyUtil; @override void initState() { super.initState(); firebasePhoneUtil = FirebasePhoneUtil(); firebasePhoneUtil.setScreenListener(this); firebaseGoogleUtil = FirebaseGoogleUtil(); firebaseGoogleUtil.setScreenListener(this); firebaseAnonymouslyUtil = FirebaseAnonymouslyUtil(); firebaseAnonymouslyUtil.setScreenListener(this); } void _submit() { { setState(() { if (_isPhoneAuthEnable) { if (_teMobileEmail.text.isEmpty) { showAlert("Enter valid mobile number"); } else { _isLoading = true; firebasePhoneUtil.verifyPhoneNumber( _teMobileEmail.text.trim(), _teCountryCode.text.trim()); } } else if (_isEmailAuthEnable && validateEmail(_teMobileEmail.text) == null) { _isLoading = true; login(_teMobileEmail.text, _tePassword.text); } }); } } String validateEmail(String value) { String pattern = r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'; RegExp regExp = new RegExp(pattern); if (value.length == 0) { AppUtil().showAlert("Email is Required"); return "Email is Required"; } else if (!regExp.hasMatch(value)) { AppUtil().showAlert("Invalid Email"); return "Invalid Email"; } else { return null; } } @override void moveUserDashboardScreen(FirebaseUser currentUser) { phoneTabEnable(); closeLoader(); Navigator.of(context).push<String>( new MaterialPageRoute( settings: RouteSettings(name: '/home_screen'), builder: (context) => UserDashboardScreen(currentUser), ), ); } @override Widget build(BuildContext context) { var tabs = new Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new GestureDetector( onTap: () { phoneTabEnable(); }, child: new SvgPicture.asset( "assets/smartphone.svg", height: 30.0, width: 30.0, color: _isPhoneAuthEnable ? new Color(0xFF2CB044) : new Color(0xFF626262), allowDrawingOutsideViewBox: true, ), ), new SizedBox( width: 10.0, ), new GestureDetector( onTap: () { gMailTabEnable(); }, child: new SvgPicture.asset( "assets/gmail.svg", height: 38.0, width: 38.0, color: _isGoogleAuthEnable ? new Color(0xFF2CB044) : new Color(0xFF626262), allowDrawingOutsideViewBox: true, ), ), new SizedBox( width: 10.0, ), new GestureDetector( onTap: () { eMailTabEnable(); }, child: new SvgPicture.asset( "assets/email.svg", height: 30.0, width: 30.0, color: _isEmailAuthEnable ? new Color(0xFF2CB044) : new Color(0xFF626262), allowDrawingOutsideViewBox: true, ), ), ], ); var phoneAuthForm = new Column( crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ new Row( children: <Widget>[ new Expanded( child: new TextFormField( controller: _teCountryCode, focusNode: _focusNodeCountryCode, decoration: InputDecoration( labelText: "Code", hintText: "Country code", fillColor: new Color(0xFF2CB044), ), ), flex: 1, ), new SizedBox( width: 10.0, ), new Expanded( child: new TextFormField( controller: _teMobileEmail, focusNode: _focusNodeMobileEmail, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: "Mobile Number", hintText: "Mobile number", fillColor: new Color(0xFF2CB044), prefixIcon: new Icon(Icons.mobile_screen_share), ), ), flex: 5, ), ], ), new SizedBox( height: 10.0, ), tabs ], ); var anonymouslyForm = new Column( crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ new TextFormField( controller: _teMobileEmail, focusNode: _focusNodeMobileEmail, decoration: InputDecoration( labelText: "Please enter email", hintText: "Email", fillColor: new Color(0xFF2CB044), prefixIcon: new Icon(Icons.email), ), ), new SizedBox( height: 10.0, ), new TextFormField( controller: _tePassword, focusNode: _focusNodePassword, decoration: InputDecoration( labelText: "Password", hintText: "Passwrod", fillColor: new Color(0xFF2CB044), prefixIcon: new Icon(Icons.keyboard_hide), ), ), new SizedBox( height: 10.0, ), tabs ], ); var googleForm = new Column( crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ new SizedBox( height: 20.0, ), new Center( child: new CircularProgressIndicator( valueColor: new AlwaysStoppedAnimation<Color>(Colors.green), ), ), new SizedBox( height: 40.0, ), tabs ], ); var loginForm = new Column( children: <Widget>[ new Container( alignment: FractionalOffset.center, margin: EdgeInsets.fromLTRB(40.0, 20.0, 40.0, 0.0), padding: EdgeInsets.fromLTRB(10.0, 20.0, 10.0, 10.0), decoration: new BoxDecoration( color: const Color.fromRGBO(255, 255, 255 border: Border.all(color: const Color(0x33A6A6A6)), borderRadius: new BorderRadius.all(const Radius.circular(6.0)), ), child: new Column( crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ _isPhoneAuthEnable ? phoneAuthForm : _isEmailAuthEnable ? anonymouslyForm : googleForm, new Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ _isEmailAuthEnable ? new GestureDetector( onTap: () { _signUp(); }, child: new Container( margin: EdgeInsets.only(top: 20.0,right: 20.0), padding: EdgeInsets.all(15.0), alignment: FractionalOffset.center, decoration: new BoxDecoration( color: new Color(0xFF2CB044), borderRadius: new BorderRadius.all( const Radius.circular(6.0)), ), child: Text( _isEmailAuthEnable ? "SIGN-UP" : "", style: new TextStyle( color: const Color(0xFFFFFFFF), fontSize: 15.0, fontWeight: FontWeight.bold), ), ), ) : new SizedBox( width: 0.0, height: 0.0, ), new GestureDetector( onTap: () { _submit(); }, child: new Container( margin: EdgeInsets.only(top: 20.0), padding: EdgeInsets.all(15.0), alignment: FractionalOffset.center, decoration: new BoxDecoration( color: new Color(0xFF2CB044), borderRadius: new BorderRadius.all(const Radius.circular(6.0)), ), child: Text( _isEmailAuthEnable ? "LOGIN" : "SUBMIT", style: new TextStyle( color: const Color(0xFFFFFFFF), fontSize: 15.0, fontWeight: FontWeight.bold), ), ), ), ], ), ], ), ), ], ); var screenRoot = new Container( height: double.maxFinite, alignment: FractionalOffset.center, child: new SingleChildScrollView( child: new Center( child: loginForm, ), ), ); return new Scaffold( backgroundColor: const Color(0xFF2B2B2B), appBar: null, key: _scaffoldKey, body: ProgressHUD( child: screenRoot, inAsyncCall: _isLoading, opacity: 0.0, ), ); } @override void onLoginError(String errorTxt) { setState(() => _isLoading = false); } @override void closeLoader() { setState(() => _isLoading = false); } @override void showAlert(String msg) { setState(() { AppUtil().showAlert(msg); }); } @override void showLoader() { setState(() => _isLoading = true); } @override verificationCodeSent(int forceResendingToken) { moveOtpVerificationScreen(); } @override onLoginUserVerified(FirebaseUser currentUser) { moveUserDashboardScreen(currentUser); } @override onError(String msg) { showAlert(msg); setState(() { _isLoading = false; }); } void phoneTabEnable() { setState(() { _isPhoneAuthEnable = true; _isEmailAuthEnable = false; _isGoogleAuthEnable = false; _teMobileEmail.text=""; }); } void gMailTabEnable() { setState(() { _isPhoneAuthEnable = false; _isEmailAuthEnable = false; _isGoogleAuthEnable = true; _teMobileEmail.text=""; firebaseGoogleUtil.signInWithGoogle(); }); } void eMailTabEnable() { setState(() { _teMobileEmail.text=""; _isPhoneAuthEnable = false; _isEmailAuthEnable = true; _isGoogleAuthEnable = false; }); } loginError(e) { setState(() { AppUtil().showAlert(e.message); _isLoading = false; }); } void moveOtpVerificationScreen() { closeLoader(); Navigator.of(context).push<String>( new MaterialPageRoute( settings: RouteSettings(name: '/otp_verification'), builder: (context) => OtpVerificationScreen(), ), ); } void _signUp() { setState(() { if (_isEmailAuthEnable && validateEmail(_teMobileEmail.text) == null) { _isLoading = true; firebaseAnonymouslyUtil .createUser(_teMobileEmail.text, _tePassword.text) .then((String user) => login(_teMobileEmail.text, _tePassword.text)) .catchError((e) => loginError(e)); } }); } login(String email, String pass) { firebaseAnonymouslyUtil .signIn(_teMobileEmail.text, _tePassword.text) .then((FirebaseUser user) => moveUserDashboardScreen(user)) .catchError((e) => loginError(e)); } }

now we have created a user interface for this project. The output of the above widget looks like:



1. When you'll tap on the first tab that's phone tab. You can get authentication with the mobile number.
2. The second tab is for Google account authentication.
3. The last tab is for Email/Password authentication. 

Let's see utility classes and it's functionalities.

Create and get authentication from the Phone number.

5. Firebase mobile number registration is base on the OTP feature of Firebase. For implementing phone authentication, you need to pay for SMS service. But with Firebase, you can do it FREE. The free plan of Firebase has Ten Thousand Verification per month. You have to enter your phone number, receives an OTP, and then you use that OTP to authenticate. To use Firebase phone authentication, we have to enable it from Firebase console and you can add a test number for app development mode. When you use test number for mobile verification. It'll not count in your monthly OTP usage. As you can see below, we have added +919999988888 and set test OTP 555555.


now create firebase_phone_util.dart that'll handle Firebase phone API's.
firebase_phone_util.dart
import 'dart:async'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter_firebase_auth/util/firebase_listenter.dart'; import 'package:google_sign_in/google_sign_in.dart'; class FirebasePhoneUtil { static final FirebasePhoneUtil _instance = new FirebasePhoneUtil.internal(); FirebasePhoneUtil.internal(); factory FirebasePhoneUtil() { return _instance; } FirebaseAuthListener _view; final FirebaseAuth _auth = FirebaseAuth.instance; String verificationId; FirebaseUser user; setScreenListener(FirebaseAuthListener view) { _view = view; } //It will send you OTP to given phone number. Future<void> verifyPhoneNumber(String phoneNumber, String code) async { final PhoneVerificationCompleted verificationCompleted = (FirebaseUser user) {}; final PhoneVerificationFailed verificationFailed = (AuthException authException) { _view.onError(authException.message); }; final PhoneCodeSent codeSent = (String verificationId, [int forceResendingToken]) async { this.verificationId = verificationId; _view.verificationCodeSent(forceResendingToken); }; final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout = (String verificationId) { _view.onError(verificationId); }; await _auth.verifyPhoneNumber( phoneNumber: code + phoneNumber, timeout: const Duration(seconds: 5), verificationCompleted: verificationCompleted, verificationFailed: verificationFailed, codeSent: codeSent, codeAutoRetrievalTimeout: codeAutoRetrievalTimeout); } //It'll verify OTP and provide you auth. verifyOtp(String smsCode) async { final FirebaseUser user = await _auth.signInWithPhoneNumber( verificationId: verificationId, smsCode: smsCode, ); final FirebaseUser currentUser = await _auth.currentUser(); if (!identical(user.uid, currentUser.uid)) { onLoginUserVerified(currentUser); } } void onLoginUserVerified(FirebaseUser currentUser) { _view.onLoginUserVerified(currentUser); } onTokenError(String string) { print("libs "+string); } }
in the above snippet, we have verifyPhoneNumber for sending a number for getting OTP and verifyOtp to verify OTP. After successfully verify OTP, you will get Firebase auth.

6. When Firebase trying to send you an OTP. You will move to the OTP verification screen. Where you have to enter valid OTP for getting auth. To complete this process, we have created following OTP verification widget.
otp_verification_screen.dart
import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter_firebase_auth/util/app_util.dart'; import 'package:flutter_firebase_auth/util/count/countdown_base.dart'; import 'package:flutter_firebase_auth/util/firebase_listenter.dart'; import 'package:flutter_firebase_auth/util/firebase_phone_util.dart'; import 'package:flutter_firebase_auth/view/user_dashboard/user_dashboard.dart'; import 'package:flutter_firebase_auth/widget/progress_hud.dart'; class OtpVerificationScreen extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( debugShowCheckedModeBanner: false, theme: new ThemeData( primarySwatch: Colors.blue, ), home: OtpVerificationScreenState(), ); } } class OtpVerificationScreenState extends StatefulWidget { @override _OtpVerificationScreenState createState() => new _OtpVerificationScreenState(); } class _OtpVerificationScreenState extends State<OtpVerificationScreenState> implements FirebaseAuthListener { bool _isLoading = false; final _formKey = new GlobalKey<FormState>(); final _scaffoldKey = new GlobalKey<ScaffoldState>(); String otpWaitTimeLabel = ""; bool _isMobileNumberEnter = false; final _teOtpDigitOne = TextEditingController(); final _teOtpDigitTwo = TextEditingController(); final _teOtpDigitThree = TextEditingController(); final _teOtpDigitFour = TextEditingController(); final _teOtpDigitFive = TextEditingController(); final _teOtpDigitSix = TextEditingController(); FocusNode _focusNodeDigitOne = new FocusNode(); FocusNode _focusNodeDigitTwo = new FocusNode(); FocusNode _focusNodeDigitThree = new FocusNode(); FocusNode _focusNodeDigitFour = new FocusNode(); FocusNode _focusNodeDigitFive = new FocusNode(); FocusNode _focusNodeDigitSix = new FocusNode(); FirebasePhoneUtil presenter; static const TextStyle linkStyle = const TextStyle( color: const Color(0xFF8C919E), fontWeight: FontWeight.bold, ); @override void dispose() { super.dispose(); _teOtpDigitOne.dispose(); } @override void initState() { super.initState(); presenter = FirebasePhoneUtil(); presenter.setScreenListener(this); changeFocusListener(_teOtpDigitOne, _focusNodeDigitTwo); changeFocusListener(_teOtpDigitTwo, _focusNodeDigitThree); changeFocusListener(_teOtpDigitThree, _focusNodeDigitFour); changeFocusListener(_teOtpDigitFour, _focusNodeDigitFive); changeFocusListener(_teOtpDigitFive, _focusNodeDigitSix); checkFiled(_teOtpDigitOne); checkFiled(_teOtpDigitTwo); checkFiled(_teOtpDigitThree); checkFiled(_teOtpDigitFour); checkFiled(_teOtpDigitFive); checkFiled(_teOtpDigitSix); startTimer(); } void checkFiled(TextEditingController teController) { teController.addListener(() { if (!_teOtpDigitOne.text.isEmpty && !_teOtpDigitTwo.text.isEmpty && !_teOtpDigitThree.text.isEmpty && !_teOtpDigitFour.text.isEmpty && !_teOtpDigitFive.text.isEmpty && !_teOtpDigitSix.text.isEmpty) { _isMobileNumberEnter = true; } else { _isMobileNumberEnter = false; } setState(() {}); }); } void _submit() { if (_isMobileNumberEnter) { showLoader(); presenter.verifyOtp(_teOtpDigitOne.text + _teOtpDigitTwo.text + _teOtpDigitThree.text + _teOtpDigitFour.text + _teOtpDigitFive.text + _teOtpDigitSix.text); } else { showAlert("Please enter valid OTP!"); } } @override Widget build(BuildContext context) { var otpBox = new Row( children: <Widget>[ new Expanded( child: new TextFormField( controller: _teOtpDigitOne, focusNode: _focusNodeDigitOne, keyboardType: TextInputType.number, ), ), new SizedBox( width: 10.0, ), new Expanded( child: new TextFormField( controller: _teOtpDigitTwo, focusNode: _focusNodeDigitTwo, textAlign: TextAlign.center, keyboardType: TextInputType.number, ), ), new SizedBox( width: 10.0, ), new Expanded( child: new TextFormField( controller: _teOtpDigitThree, focusNode: _focusNodeDigitThree, keyboardType: TextInputType.number, textAlign: TextAlign.center, ), ), new SizedBox( width: 10.0, ), new Expanded( child: new TextFormField( controller: _teOtpDigitFour, focusNode: _focusNodeDigitFour, keyboardType: TextInputType.number, textAlign: TextAlign.center, ), ), new SizedBox( width: 10.0, ), new Expanded( child: new TextFormField( controller: _teOtpDigitFive, focusNode: _focusNodeDigitFive, keyboardType: TextInputType.number, textAlign: TextAlign.center, ), ), new SizedBox( width: 10.0, ), new Expanded( child: new TextFormField( controller: _teOtpDigitSix, focusNode: _focusNodeDigitSix, keyboardType: TextInputType.number, ), ), ], ); var form = new Column( children: <Widget>[ new Container( alignment: FractionalOffset.center, margin: EdgeInsets.fromLTRB(40.0, 50.0, 40.0, 0.0), padding: EdgeInsets.all(20.0), decoration: new BoxDecoration( color: const Color(0xFFF9F9F9), borderRadius: new BorderRadius.all( const Radius.circular(6.0), ), ), child: new Form( key: _formKey, child: new Column( children: <Widget>[ new Text( "Enter valid recieved OTP", ), new Padding( padding: EdgeInsets.only(left: 30.0, right: 30.0, top: 20.0), child: otpBox, ), new SizedBox( width: 0.0, height: 20.0, ), new Text( otpWaitTimeLabel, ), new GestureDetector( onTap: () { _submit(); }, child: new Container( margin: EdgeInsets.only(top: 20.0), padding: EdgeInsets.all(15.0), alignment: FractionalOffset.center, decoration: new BoxDecoration( color: new Color(0xFF2CB044), borderRadius: new BorderRadius.all(const Radius.circular(6.0)), ), child: Text( "Verify OTP", style: new TextStyle( color: const Color(0xFFFFFFFF), fontSize: 15.0, fontWeight: FontWeight.bold), ), ), ), ], ), ), ), ], ); var screenRoot = new Container( height: double.maxFinite, alignment: FractionalOffset.center, child: new SingleChildScrollView( child: new Center( child: form, ), ), ); return new WillPopScope( onWillPop: () async { print("back"); return true; }, child: new Scaffold( backgroundColor: const Color(0xFF2B2B2B), appBar: null, key: _scaffoldKey, body: ProgressHUD( child: screenRoot, inAsyncCall: _isLoading, opacity: 0.0, ), )); } void changeFocusListener( TextEditingController teOtpDigitOne, FocusNode focusNodeDigitTwo) { teOtpDigitOne.addListener(() { if (teOtpDigitOne.text.length > 0 && focusNodeDigitTwo != null) { FocusScope.of(context).requestFocus(focusNodeDigitTwo); } setState(() {}); }); } @override void closeLoader() { setState(() => _isLoading = false); } @override void showAlert(String msg) { setState(() { AppUtil().showAlert(msg); }); } @override void showLoader() { setState(() => _isLoading = true); } @override onError(String msg) { showAlert(msg); closeLoader(); } void startTimer() { var sub = new CountDown(new Duration(minutes: 5)).stream.listen(null); sub.onData((Duration d) { setState(() { int sec = d.inSeconds % 60; otpWaitTimeLabel = d.inMinutes.toString() + ":" + sec.toString(); }); }); } @override verificationCodeSent(int forceResendingToken) {} @override onLoginUserVerified(FirebaseUser currentUser) async { Navigator.of(context) .push<String>( new MaterialPageRoute( settings: RouteSettings(name: '/home_screen'), builder: (context) => UserDashboardScreen(currentUser), ), ) .then((result) => setState(() { // _result = result; })); } }
the output of the above widget look like below:

flutter firebase phone otp screen

when you will enter valid OTP, you will get auth and move to welcome screen of user dashboard. As you can see above video.


Create and get authentication from Google account.

6. Here, we have another util firebase_google_util.dart file. With the help of this util class, we can get auth from google account.
firebase_google_util.dart
import 'dart:async'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter_firebase_auth/util/firebase_listenter.dart'; import 'package:google_sign_in/google_sign_in.dart'; class FirebaseGoogleUtil { static final FirebaseGoogleUtil _instance = new FirebaseGoogleUtil.internal(); final GoogleSignIn _googleSignIn = new GoogleSignIn(); final FirebaseAuth _auth = FirebaseAuth.instance; FirebaseAuthListener _view; FirebaseGoogleUtil.internal(); factory FirebaseGoogleUtil() { return _instance; } setScreenListener(FirebaseAuthListener view) { _view = view; } Future<void> signInWithGoogle() async { GoogleSignInAccount googleUser = await _googleSignIn.signIn(); GoogleSignInAuthentication googleAuth = await googleUser.authentication; FirebaseUser user = await _auth.signInWithGoogle( accessToken: googleAuth.accessToken, idToken: googleAuth.idToken, ); print("signed in " + user.displayName); assert(user.email != null); assert(user.displayName != null); assert(!user.isAnonymous); assert(await user.getIdToken() != null); final FirebaseUser currentUser = await _auth.currentUser(); if (!identical(user.uid, currentUser.uid)) { onLoginUserVerified(currentUser); } else { onTokenError(user.toString()); } } void onLoginUserVerified(FirebaseUser currentUser) { _view.onLoginUserVerified(currentUser); } onTokenError(String string) { _view.onError(string); } }
to get auth from google account, we have created signInWithGoogle method. It'll show a list of login user in a device. Once you tab one on these, you will sign-up and login in onLoginUserVerified. After that, you'll move to welcome screen of user dashboard as shown in the above video.

Create and get authentication from Email/Password.

7. Now we have created email and password util firebase_anonymously_util.dart file. In this file, we have sign-up, login and signOut method for Firebase Email/Password authentication. Before going to use it, we have to enable it from Firebase console as you see above. We have enabled Email/Password Firebase authentication. You can use email/Password auth feature when you tap on email tab of the app. As you can see below.

 here, we have the complete snippet for email/password.
firebase_anonymously_util.dart
import 'dart:async'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter_firebase_auth/util/firebase_listenter.dart'; class FirebaseAnonymouslyUtil { static final FirebaseAnonymouslyUtil _instance = new FirebaseAnonymouslyUtil.internal(); FirebaseAuth _firebaseAuth = FirebaseAuth.instance; FirebaseAuthListener _view; FirebaseAnonymouslyUtil.internal(); factory FirebaseAnonymouslyUtil() { return _instance; } setScreenListener(FirebaseAuthListener view) { _view = view; } Future<FirebaseUser> signIn(String email, String password) async { FirebaseUser user = await _firebaseAuth.signInWithEmailAndPassword( email: email, password: password); return user; } Future<String> createUser(String email, String password) async { FirebaseUser user = await _firebaseAuth.createUserWithEmailAndPassword( email: email, password: password); return user.uid; } Future<String> currentUser() async { FirebaseUser user = await _firebaseAuth.currentUser(); return user != null ? user.uid : null; } Future<void> signOut() async { return _firebaseAuth.signOut(); } void onLoginUserVerified(FirebaseUser currentUser) { _view.onLoginUserVerified(currentUser); } onTokenError(String string) { _view.onError(string); } }
once you entered any email/password to create and login with an existing user. You will move to following the user_dashboard.dart screen. 

8. In this class, we are showing details of logged user like email, phone number, and google account email id.
user_dashboard.dart
import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; class UserDashboardScreen extends StatelessWidget { FirebaseUser currentUser; UserDashboardScreen(this.currentUser); @override Widget build(BuildContext context) { return new MaterialApp( debugShowCheckedModeBanner: false, theme: new ThemeData( primaryColor: const Color(0xFFF47C06), primaryColorDark: const Color(0xFFFFCA2B), ), home: UserDashboard(currentUser), ); } } class UserDashboard extends StatefulWidget { FirebaseUser currentUser; UserDashboard(this.currentUser); @override _MyHomePageState createState() => new _MyHomePageState(currentUser); } class _MyHomePageState extends State<UserDashboard> { FirebaseUser currentUser; _MyHomePageState(this.currentUser); String lebel; @override void initState() { super.initState(); if (currentUser.phoneNumber != null) { lebel = "Welcome\n" + currentUser.phoneNumber; } else if (currentUser.displayName != null) { lebel = "Welcome\n" + currentUser.displayName; } else { lebel = "Welcome\n" + currentUser.email; } } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text( 'Flutter Auth', style: new TextStyle(color: Colors.white), ), ), body: new Center( child: new Text( lebel, style: new TextStyle(fontSize: 30.0), textAlign: TextAlign.center, ), ), ); } }



In this post, I have explained Firebase authentication with the help of Email/Password, Phone number, and Google account. To use each feature, we have a separate util file and defined its method. You can get the working source code and Android APK to check this example.

Flutter Firebase auth example source code               Flutter Firebase auth example

I have tried to make things as simple as possible. You can use this in your Firebase based Flutter app as the base structure,. You can add or modify the feature according to your requirements. 

I hope, you got the basic idea for a Firebase Authentication app in the Flutter. If you have any doubt and query, please feel free to ask it from the comment section below and you can ask it on SLACK.

Share:

Get it on Google Play

React Native - Start Development with Typescript

React Native is a popular framework for building mobile apps for both Android and iOS. It allows developers to write JavaScript code that ca...