In this flutter tutorial, we will learn flutter firebase authentication. Using Email and Password, we will use GETX State Management to perform firebase authentication in a flutter.
- There will be many topics that we will cover in this playlist and they are as follows.
- Create a user with an email and password
- Sign In user with email and password
- Logout the user
- Keep the firebase user logged in even app closed
- Keep the separation of concern with a separate Authentication Repository Controller
- Signup, and login controller with the backend logic.
Watch Youtube tutorial
Step 1: Setup Firebase Authentication
Before directly starting with firebase authentication, you need to have few things up and running.
We are creating Flutter Login App and we have already learned and designed the Login App. You can download the code from here, or watch the playlist here.
Firstly, you have to setup firebase in flutter.
Enable the Email SignIn method in Firebase Console.
Add firebase_auth dependency in the pubspec.ymal file in your flutter project.
Once everything setup, now let’s configure main.dart because whenever our project loads, it should load firebase.
Therefore, we will add WidgetsFlutterBinding.ensureInitialized(); so that our flutter widgets wait until firebase initilization completed and afterwards, we will call the firebase like
Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
- signup_form.dart
- signup_controller.dart
- authentication_repository.dart
- login_screen.dart
- login_form_widget.dart
- form_header_widget.dart
- login_controller.dart
class SignUpFormWidget extends StatelessWidget {
const SignUpFormWidget({
Key? key,
}) : super(key: key);@override
Widget build(BuildContext context) {
final controller = Get.put(SignUpController());
final _formKey = GlobalKey<FormState>();return Container(
padding: const EdgeInsets.symmetric(vertical: tFormHeight - 10),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
controller: controller.fullName,
decoration: const InputDecoration(label: Text(tFullName), prefixIcon: Icon(Icons.person_outline_rounded)),
),
const SizedBox(height: tFormHeight - 20),
TextFormField(
controller: controller.email,
decoration: const InputDecoration(label: Text(tEmail), prefixIcon: Icon(Icons.email_outlined)),
),
const SizedBox(height: tFormHeight - 20),
TextFormField(
controller: controller.phoneNo,
decoration: const InputDecoration(label: Text(tPhoneNo), prefixIcon: Icon(Icons.numbers)),
),
const SizedBox(height: tFormHeight - 20),
TextFormField(
controller: controller.password,
decoration: const InputDecoration(label: Text(tPassword), prefixIcon: Icon(Icons.fingerprint)),
),
const SizedBox(height: tFormHeight - 10),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
if(_formKey.currentState!.validate()){
SignUpController.instance.registerUser(controller.email.text.trim(), controller.password.text.trim());
}
},
child: Text(tSignup.toUpperCase()),
),
)
],
),
),
);
}
}
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../../repository/authentication_repository/authentication_repository.dart'; class SignUpController extends GetxController { static SignUpController get instance => Get.find(); //TextField Controllers to get data from TextFields final email = TextEditingController(); final password = TextEditingController(); final fullName = TextEditingController(); final phoneNo = TextEditingController(); //Call this Function from Design & it will do the rest void registerUser(String email, String password) { String? error = AuthenticationRepository.instance.createUserWithEmailAndPassword(email, password) as String?; if(error != null) { Get.showSnackbar(GetSnackBar(message: error.toString(),)); } } }
import 'package:firebase_auth/firebase_auth.dart';
import 'package:get/get.dart';
import 'package:login_flutter_app/src/features/authentication/screens/welcome/welcome_screen.dart';
import 'package:login_flutter_app/src/features/core/screens/dashboard/dashboard.dart';import 'exceptions/login_with_email_and_pssword_failure.dart';
import 'exceptions/signup_email_password_failure.dart';class AuthenticationRepository extends GetxController {
static AuthenticationRepository get instance => Get.find();//Variables
final _auth = FirebaseAuth.instance;
late final Rx<User?> firebaseUser;//Will be load when app launches this func will be called and set the firebaseUser state
@override
void onReady() {
firebaseUser = Rx<User?>(_auth.currentUser);
firebaseUser.bindStream(_auth.userChanges());
ever(firebaseUser, _setInitialScreen);
}/// If we are setting initial screen from here
/// then in the main.dart => App() add CircularProgressIndicator()
_setInitialScreen(User? user) {
user == null ? Get.offAll(() => const WelcomeScreen()) : Get.offAll(() => const Dashboard());
}//FUNC
Future<String?> createUserWithEmailAndPassword(String email, String password) async {
try {
await _auth.createUserWithEmailAndPassword(email: email, password: password);
firebaseUser.value != null ? Get.offAll(() => const Dashboard()) : Get.to(() => const WelcomeScreen());
} on FirebaseAuthException catch (e) {
final ex = SignUpWithEmailAndPasswordFailure.code(e.code);
return ex.message;
} catch (_) {
const ex = SignUpWithEmailAndPasswordFailure();
return ex.message;
}
return null;
}Future<String?> loginWithEmailAndPassword(String email, String password) async {
try {
await _auth.signInWithEmailAndPassword(email: email, password: password);
} on FirebaseAuthException catch (e) {
final ex = LogInWithEmailAndPasswordFailure.fromCode(e.code);
return ex.message;
} catch (_) {
const ex = LogInWithEmailAndPasswordFailure();
return ex.message;
}
return null;
}Future<void> logout() async => await _auth.signOut();
}
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:login_flutter_app/src/common_widgets/form/form_header_widget.dart'; import 'package:login_flutter_app/src/common_widgets/form/social_footer.dart'; import 'package:login_flutter_app/src/constants/image_strings.dart'; import 'package:login_flutter_app/src/constants/text_strings.dart'; import 'package:login_flutter_app/src/features/authentication/screens/signup/signup_screen.dart'; import '../../../../common_widgets/form/form_divider_widget.dart'; import '../../../../constants/sizes.dart'; import 'widgets/login_form_widget.dart'; class LoginScreen extends StatelessWidget { const LoginScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( body: SingleChildScrollView( child: Container( padding: const EdgeInsets.all(tDefaultSpace), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const FormHeaderWidget(image: tWelcomeScreenImage, title: tLoginTitle, subTitle: tLoginSubTitle), const LoginFormWidget(), const TFormDividerWidget(), SocialFooter(text1: tDontHaveAnAccount, text2: tSignup, onPressed: () => Get.off(() => const SignupScreen())), ], ), ), ), ), ); } }
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:line_awesome_flutter/line_awesome_flutter.dart'; import 'package:login_flutter_app/src/common_widgets/buttons/primary_button.dart'; import 'package:login_flutter_app/src/features/authentication/controllers/login_controller.dart'; import '../../../../../constants/sizes.dart'; import '../../../../../constants/text_strings.dart'; import '../../../../../utils/helper/helper_controller.dart'; import '../../forget_password/forget_password_options/forget_password_model_bottom_sheet.dart'; class LoginFormWidget extends StatelessWidget { const LoginFormWidget({ Key? key, }) : super(key: key); @override Widget build(BuildContext context) { final controller = Get.put(LoginController()); return Container( padding: const EdgeInsets.symmetric(vertical: tFormHeight), child: Form( key: controller.loginFormKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ /// -- Email Field TextFormField( validator: Helper.validateEmail, controller: controller.email, decoration: const InputDecoration(prefixIcon: Icon(LineAwesomeIcons.user), labelText: tEmail, hintText: tEmail), ), const SizedBox(height: tFormHeight - 20), /// -- Password Field Obx( () => TextFormField( controller: controller.password, validator: (value) { if (value!.isEmpty) return 'Enter your password'; return null; }, obscureText: controller.showPassword.value ? false : true, decoration: InputDecoration( prefixIcon: const Icon(Icons.fingerprint), labelText: tPassword, hintText: tPassword, suffixIcon: IconButton( icon: controller.showPassword.value ? const Icon(LineAwesomeIcons.eye) : const Icon(LineAwesomeIcons.eye_slash), onPressed: () => controller.showPassword.value = !controller.showPassword.value, ), ), ), ), const SizedBox(height: tFormHeight - 20), /// -- FORGET PASSWORD BTN Align( alignment: Alignment.centerRight, child: TextButton( onPressed: () => ForgetPasswordScreen.buildShowModalBottomSheet(context), child: const Text(tForgetPassword), ), ), /// -- LOGIN BTN Obx( () => TPrimaryButton( isLoading: controller.isLoading.value ? true : false, text: tLogin.tr, onPressed: controller.isFacebookLoading.value || controller.isGoogleLoading.value ? () {} : controller.isLoading.value ? () {} : () => controller.login(), ), ), ], ), ), ); } }
import 'package:flutter/material.dart';
class FormHeaderWidget extends StatelessWidget {
const FormHeaderWidget({
Key? key,
this.imageColor,
this.heightBetween,
required this.image,
required this.title,
required this.subTitle,
this.imageHeight = 0.15,
this.textAlign,
this.crossAxisAlignment = CrossAxisAlignment.start,
}) : super(key: key);
//Variables -- Declared in Constructor
final Color? imageColor;
final double imageHeight;
final double? heightBetween;
final String image, title, subTitle;
final CrossAxisAlignment crossAxisAlignment;
final TextAlign? textAlign;
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Column(
crossAxisAlignment: crossAxisAlignment,
children: [
Image(image: AssetImage(image), color: imageColor, height: size.height * imageHeight),
SizedBox(height: heightBetween),
Text(title, style: Theme.of(context).textTheme.displayLarge),
Text(subTitle, textAlign: textAlign, style: Theme.of(context).textTheme.bodyLarge),
],
);
}
}
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../../repository/authentication_repository/authentication_repository.dart'; class LoginController extends GetxController { static LoginController get instance => Get.find(); /// TextField Controllers to get data from TextFields final email = TextEditingController(); final password = TextEditingController(); /// TextField Validation //Call this Function from Design & it will do the rest Future<void> login() async { String? error = await AuthenticationRepository.instance.loginWithEmailAndPassword(email.text.trim(), password.text.trim()); if(error != null) { Get.showSnackbar(GetSnackBar(message: error.toString(),)); } } }
6 – Clean Code
So far we were creating everything inside dashboard.dart but at the end you will see our code size has increased alot and become unmanageable.
Therefore, we have to convert our code into separate Custom widgets.
OTHER USEFUL LINKS
- Install Android Studio
- Install Flutter
- Create a new flutter app
- Create Folder Structure
- Setup Theme in Flutter for Light & Dark Mode
This is the tutorial of our Flutter Login App Series.