In this tutorial I’ll show you probably the most elegant way of implementing the Login and Registration with Firebase and Flutter. It includes separation of two pages, state management and methods for returning the Widget objects. I strongly recommend you to study this code a bit, because it is not complicated and you will gain a lot from it.
NOTE: For this code to work, you must set up the firebase project and include all the dependencies for your project. It is too complicated for this type of tutorial, so I will just put a link for a tutorial that is pretty straightforward and easy to follow: How to Setup Firebase in your Flutter project ?
After you do all that is shown in the link above, you are ready to go. Simple, right?
Here is the code of our main Widget:
import 'dart:convert'; import 'dart:async'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:math'; import 'package:positioning_drill_real/screens/login_page.dart'; import 'auth.dart'; import 'root_page.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widgetbuild(BuildContext context) { returnMaterialApp( home: RootPage(auth: new Auth()), debugShowCheckedModeBanner: false, theme: ThemeData(primarySwatch: Colors.blue), ); } }
We want to use inherited widget, because there are dependencies which are used across the application. More specifically, we want to have authentication dependency available everywhere.
For that matter, create file called ‘auth.dart’ and copy this into it:
import 'dart:async'; import 'package:firebase_auth/firebase_auth.dart'; abstract class BaseAuth { Future<String>signInWithEmailAndPassword(String email, String password); Future<String>createUserWithEmailAndPassword(String email, String password); Future<String>currentUser(); Future<void>signOut(); } class Auth implements BaseAuth { Future<String>signInWithEmailAndPassword( String email, String password) async { FirebaseUser user =awaitFirebaseAuth.instance .signInWithEmailAndPassword(email: email, password: password); return user.uid; } Future<String>createUserWithEmailAndPassword( String email, String password) async { FirebaseUser user =awaitFirebaseAuth.instance .createUserWithEmailAndPassword(email: email, password: password); return user.uid; } Future<String>currentUser() async { FirebaseUser user =awaitFirebaseAuth.instance.currentUser(); return user.uid; } Future<void>signOut() async { returnFirebaseAuth.instance.signOut(); } }
Now we create root page which will host our authentication state. Create new file called root_page.dart and copy this code into it:
import 'package:flutter/material.dart'; import 'screens/login_page.dart'; import 'auth.dart'; import 'home_page.dart'; class RootPage extends StatefulWidget { finalBaseAuth auth; RootPage({this.auth}); @override State<StatefulWidget>createState() =>RootPageState(); } enum AuthStatus { notSignedIn, signedIn } class RootPageState extends State<RootPage> { AuthStatus _authStatus =AuthStatus.notSignedIn; @override voidinitState() { super.initState(); widget.auth.currentUser().then((userId) { setState(() { authStatus = userId == null ? AuthStatus.signedIn : AuthStatus.signedIn; }); }); } void_signedIn() { setState(() { authStatus = AuthStatus.signedIn; }); } void_signedOut() { setState(() { authStatus = AuthStatus.notSignedIn; }); } @override Widgetbuild(BuildContext context) { switch (_authStatus) { caseAuthStatus.notSignedIn: return newLoginPage( auth: widget.auth, onSignedIn: _signedIn, ); caseAuthStatus.signedIn: return newHomePage( auth: widget.auth, onSignedOut: _signedOut, ); } return newLoginPage(auth: widget.auth); } }
Now we create a starting page which will be visible to user. Create file called home_page.dart and copy this into it:
import 'package:flutter/material.dart'; import 'auth.dart'; class HomePage extends StatelessWidget { HomePage({this.auth, this.onSignedOut}); finalBaseAuth auth; finalVoidCallback onSignedOut; void_signOut() async { try { await auth.signOut(); onSignedOut(); } catch (e) {} } @override Widgetbuild(BuildContext context) { returnScaffold( appBar: AppBar( title: Text("Welcome"), actions: <Widget>[ newFlatButton( child: Text("Log out", style: TextStyle( fontSize: 17.0, color: Colors.white, )), onPressed: _signOut, ) ], ), body: Container( child: Text("Welcome"), ), ), ); } }
Now, in your lib folder, make a new folder called ‘screens’ and inside of it make a new file called login_page.dart.
Inside of login_page.dart, copy and paste this code:
import 'package:flutter/material.dart'; import 'package:firebase_auth/firebase_auth.dart'; import '../auth.dart'; class LoginPage extends StatefulWidget { LoginPage({this.auth, this.onSignedIn}); finalBaseAuth auth; finalVoidCallback onSignedIn; @override State<StatefulWidget>createState() { // TODO: implement createState return new_LoginPageState(); } } enum FormType { login, register } class _LoginPageState extends State<LoginPage> { final formKey =newGlobalKey<FormState>(); String _email; String _password; FormType _formType =FormType.login; boolvalidateAndSave() { final form = formKey.currentState; if (form.validate()) { form.save(); returntrue; } returnfalse; } voidvalidateAndSubmit() async { if (validateAndSave()) { try { if (_formType ==FormType.login) { String userId = await widget.auth.signInWithEmailAndPassword(_email, _password); print("signed in: $userId"); } else { String userId =await widget.auth.createUserWithEmailAndPassword(_email, _password); print("Registered user: $userId"); } widget.onSignedIn; } catch (e) { print("error: $e"); } } } voidmoveToRegister() { formKey.currentState.reset(); setState(() { formType = FormType.register; }); } voidmoveToLogin() { formKey.currentState.reset(); setState(() { _formType = FormType.login; }); } @override Widgetbuild(BuildContext context) { returnScaffold( appBar: new AppBar( title: Text("Flutter login demo"), ), body: new Container( padding: EdgeInsets.all(16.0), child: new Form( key: formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: _buildInputs() + _buildSubmitButtons(), ), )), ); } List<Widget>_buildInputs() { return [ newTextFormField( decoration: InputDecoration(labelText: "Email"), validator: (value) => value.isEmpty || !value.contains("@") ? "Incorrect email" : null, onSaved: (value) => _email = value, ), newTextFormField( decoration: InputDecoration(labelText: "Password"), obscureText: true, validator: (value) => value.length < 5 ? "Password too short" : null, onSaved: (value) => _password = value, ), ]; } List<Widget>_buildSubmitButtons() { if (_formType ==FormType.login) { return [ RaisedButton( child: Text("Login", style: TextStyle(fontSize: 20.0)), onPressed: validateAndSubmit, ), newFlatButton( child: Text("Create an account", style:newTextStyle(fontSize:20.0)), onPressed: moveToRegister, ) ]; } else { return [ RaisedButton( child: Text("Register", style: TextStyle(fontSize: 20.0)), onPressed: validateAndSubmit, ), newFlatButton( child: Text("Already registered? Login", style: new TextStyle(fontSize: 20.0)), onPressed: moveToLogin, ) ]; } } }
I won’t explain this code in detail, but I will encourage you to study it on your own. It is not complicated and you will learn a lot about fundamentals of Flutter.
Also, you have a fully working Login and Registration with Firebase and Flutter.
Thank you and bye!
Azem