Geek Culture
Published in

Geek Culture

My Welcome back to Flutter App work#2

image from morioh.com

This article is a continuation of the previous article that I wrote about the same topic. If you have not read that, please go ahead and give it a read so that you can follow along with this one.
Whether you’re developing a simple application or building your dream app, authentication helps you personalize the experience with user-specific information. It’s also a crucial component for privacy and security.

🤔 Did you know?

Firebase Authentication is a pre-configured backend service that makes it easy to integrate with a mobile app using an SDK. You don’t have to maintain any backend infrastructure for the authentication process. Firebase supports integration with popular identity providers such as Google, Facebook, Twitter, and GitHub.

In this article, we will integrate authentication using Firebase in our flutter app.

Let’s set up Firebase.

Step 1: Create Firebase project
To integrate Firebase with a Flutter project, we have to create a new Firebase project by going to the console. Then, add a new project and give it a name(preferably your flutter project’s name). Once created, you will be directed to your Firebase project dashboard like the image below. Although, I’ve already added a Firebase project displayed in the picture.

firebase dashboard

Step 2: Set up Firebase for Android and iOS

To use Firebase with Android, or iOS you have to complete some configurations for each platform. See the complete configuration guides below:

Now that we have the basic setup for using Firebase, let’s dive into our Flutter app.

Adding Firebase plugins

In your pubspec.yaml , add the following plugins:

firebase_core: ^1.3.0
firebase_auth: ^2.0.0
  • firebase_core, which is required to use any Firebase service in a Flutter app
  • firebase_auth to get access to Firebase Authentication services

Initialize Firebase App

Before using any Firebase service within the Flutter app, you need to initialize Firebase App.

In the Auth.dart , add this method to initialize Firebase App:

Future<FirebaseApp> _initializeFirebase() async {
FirebaseApp firebaseApp = await Firebase.initializeApp();
return firebaseApp;
}

Since the method is asynchronous we will use FutureBuilder inside the build method

child: Scaffold(
body: FutureBuilder(
future: _inializeFirebase(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Container(
color: Colors.white,
child: CustomPaint(
painter: BackgroundPaint(),

Up next, create a new dart file called authentication.dart and define a new method called registerUsingEmailPassword() for new users to register.

class Authentication {
// For registering a new user
static Future<User?> registerUsingEmailPassword({
required String email,
required String password,
}) async {
FirebaseAuth auth = FirebaseAuth.instance;
User? user;

try {
UserCredential userCredential = await auth.createUserWithEmailAndPassword(
email: email,
password: password,
);

user = userCredential.user;
// ignore: deprecated_member_use
await user!.updateProfile(displayName: email);
await user.reload();
user = auth.currentUser;
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {
print('The password provided is too weak.');
} else if (e.code == 'email-already-in-use') {
print('The account already exists for that email.');
}
} catch (e) {
print(e);
}

return user;
}

In the above code, we register a new user using the email and password and include a conditional statement to catch errors.

Next, define a new method calledsignInUsingEmailPassword(), passing the user email and password for registered users:

// For signing in an user (have already registered)
static Future<User?> signInUsingEmailPassword({
required String email,
required String password,
}) async {
FirebaseAuth auth = FirebaseAuth.instance;
User? user;

try {
UserCredential userCredential = await auth.signInWithEmailAndPassword(
email: email,
password: password,
);
user = userCredential.user;
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
print('No user found.');
} else if (e.code == 'wrong-password') {
print('Wrong password');
}
}

return user;
}

The email and password help to generate the User object provided by Firebase. The Userobject will be used to retrieve any additional data (e.g., user name, profile picture, etc.) stored in that account.

The signOut()method logs out a user. Creating a signout method is not needed since it’s just a single line of code.

FirebaseAuth.instance.signOut();

For email verification, we will use the sendEmailVerification() method on the User object:

user.sendEmailVerification();

Next, define a validator for the form

In the previous article, we created a login/sign-up page containing two form fields: email and password. Now, we’ll create a validator for each one. The validators will help to check whether the user has entered any inappropriate value in a specific field and show an error accordingly.

Create a new file called validator.dart, define a class Validator, and specify two methods inside it (each of which will take a String as a parameter):

class Validator {
static String? validateEmail({required String? email}) {
if (email == null) {
return null;
}

RegExp emailRegExp = RegExp(
r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$");

if (email.isEmpty) {
return 'Email can\'t be empty';
} else if (!emailRegExp.hasMatch(email)) {
return 'Enter a correct email';
}

return null;
}

static String? validatePassword({required String? password}) {
if (password == null) {
return null;
}

if (password.isEmpty) {
return 'Password can\'t be empty';
} else if (password.length < 6) {
return 'Enter a password with length at least 6';
}

return null;
}
}

In the Auth.dartdefine a GlobalKey:

final _formKey = GlobalKey<FormState>();

Specify the key in the form

child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
controller: _emailTextController,
// ignore: missing_return
focusNode: _focusEmail,
validator: (value) =>
Validator.validateEmail(email: value),
decoration: InputDecoration(
labelText: 'email',
labelStyle: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w400),
filled: true,
contentPadding: EdgeInsets.symmetric(
vertical: 0, horizontal: 16),
border: OutlineInputBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25),
topRight: Radius.circular(25),
),
borderSide: BorderSide(
width: 0,
style: BorderStyle.none,
),
),

We called the validator method to check if the email is valid. We will do the same for the password field

TextFormField(
controller: _passwordTextController,
focusNode: _focusPassword,
validator: (value) =>
Validator.validatePassword(
password: value),
decoration: InputDecoration(
labelText: 'password',
labelStyle: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w400),
filled: true,
contentPadding: EdgeInsets.symmetric(
vertical: 0, horizontal: 16),
border: OutlineInputBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(25),
bottomRight: Radius.circular(25),
),
borderSide: BorderSide(
width: 0,
style: BorderStyle.none,
),
),
),
obscureText: true,
),

Inside the login button, we called FireAuth.signInUsingEmailPassword()to perform the login process using Firebase Authentication.

_isProcessing
? LoadingIndicator()
: GestureDetector(
onTap: () async {
_focusEmail.unfocus();
_focusPassword.unfocus();
if (_formKey.currentState!
.validate()) {
setState(() {
_isProcessing = true;
});
User? user = await Authentication
.signInUsingEmailPassword(
email:
_emailTextController
.text,
password:
_passwordTextController
.text);
setState(() {
_isProcessing = false;
});
if (user != null) {
Navigator.of(context)
.pushReplacement(
MaterialPageRoute(
builder: (context) =>
HomePage(user: user),
),
);
}
}
},
child: Container(
height: 40,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(25),
),
color: Color(0xff8705bf),
boxShadow: [
BoxShadow(
offset: Offset(0, 3),
blurRadius: 3,
spreadRadius: 2,
color: Color(0xffbf63d8)),
],
),
child: Center(
child: Text(
'login'.toUpperCase(),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
color: Colors.white,
),
),
),

The signup page will also contain a form similar to the login page.

Wow! That was a lot. Now, we have successfully integrated Firebase Authentication with our Flutter app. As you know, Firebase Authentication not only provides the backend infrastructure for authenticating users easily but also the predefined methods for auto-login and email verification. With Firebase, there’s a lot more to explore.

One last step to go!

We need to create a profile setting page to verify email and also, add a profile image.

Create a new dart file profile_setting.dart

First, we will need the image_picker plugin for picking images via Camera and Photo Library.

Step 1: Add the plugin to your pubspec.yaml file.

image_picker: ^0.8.2

Step 2: configure native settings

On the Android platform, no configuration is needed. For iOS, open the Info.plist file found under IOS/Runner folder and add the following keys.

<key>NSPhotoLibraryUsageDescription</key>
<string>Allow access to photo library</string>
<key>NSCameraUsageDescription</key>
<string>Allow access to camera to capture photos</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allow access to microphone</string>

Step 3: Create UI for profile_setting.dart

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';

class Settings extends StatefulWidget {
@override
_SettingsState createState() => _SettingsState();
}

class _SettingsState extends State<Settings> {
File? _avatarImage;

final picker = ImagePicker();

// create two functions to pick image from gallery/camera

// create a future function. store the result in a variable of File
// as it will return the image Path
Future pickImageFromGallery() async {
final pickedFile = await picker.pickImage(
source: ImageSource.gallery,
imageQuality: 50,
);

setState(() {
_avatarImage = File(pickedFile!.path);
});
}

Future pickImageFromCamera() async {
XFile? pickedFile = await picker.pickImage(
source: ImageSource.camera,
imageQuality: 50,
);

setState(() {
_avatarImage = pickedFile as File;
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
leading: IconButton(
icon: Icon(
FontAwesomeIcons.arrowLeft,
color: Color(0xff8705bf),
),
onPressed: () {
Navigator.pop(context);
},
),
),

In the class state, we declare a File variable to hold the image picked by the user. Then, we create two functions to pick images via Camera and Photo Library.

body: Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(
height: 100,
),
Center(
child: GestureDetector(
onTap: () => _displayPicker(context),
child: CircleAvatar(
radius: 55,
// ignore: unnecessary_null_comparison
child: _avatarImage != null
? ClipOval(
child: SizedBox(
width: 120,
height: 120,
child: Image.file(
_avatarImage!,
fit: BoxFit.fill,
),
),
)
: Container(
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(50),
),
width: 100,
height: 100,
child: Icon(
Icons.camera_alt_rounded,
color: Colors.grey.shade400,
),
),
),
),
),

We create a CircleAvatar,wrap its child with ClipOvarto clip the image and a camera icon is displayed if no picture is selected.

// create a function for displaying a bottom sheet for the user
// To choose the option of camera or gallery.
void _displayPicker(context) {
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return SafeArea(
child: Container(
child: Wrap(
children: <Widget>[
ListTile(
leading: new Icon(Icons.photo_library),
title: new Text('Gallery'),
onTap: () {
pickImageFromGallery();
Navigator.of(context).pop();
}),
ListTile(
leading: new Icon(Icons.photo_camera),
title: new Text('Camera'),
onTap: () {
pickImageFromCamera();
Navigator.of(context).pop();
},
),
],
),
),
);
},
);
}

When the CircleAvatar is tapped, a container is displayed right at the bottom to choose the option of camera or gallery. Then the selected image is displayed in the CircleAvatar.

Now, let’s write some codes to verify user email.

First, add the following code. The User object will help retrieve the user’s data.

final User user;
const Settings({required this.user});

Create a variable _currentUser and initialize it in the initState().

var _currentUser;
bool _isSendingVerification = false;

@override
void initState() {
// TODO: implement initState
_currentUser = widget.user;
super.initState();
}

The button for sending email verification is as follows:

Text(
'${_currentUser.email}',
style: Theme.of(context).textTheme.bodyText1,
),
SizedBox(
height: 16,
),
_currentUser.emailVerified
? Text(
'Email verified',
style: Theme.of(context)
.textTheme
.bodyText1!
.copyWith(color: Colors.green),
)
: Text(
'Email not verified',
style: Theme.of(context)
.textTheme
.bodyText1!
.copyWith(color: Colors.red),
),
SizedBox(
height: 16,
),
_isSendingVerification
? CircularProgressIndicator()
: Row(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: () async {
setState(() {
_isSendingVerification = true;
});
await _currentUser.sendEmailVerification();
setState(() {
_isSendingVerification = false;
});
},
child: Text(
'Verify email',
style: TextStyle(
color: Color(0xff8705bf),
fontSize: 16,
fontWeight: FontWeight.bold),
),
),
SizedBox(
width: 8.0,
),
IconButton(
onPressed: () async {
User? user =
await Authentication.refreshUser(_currentUser);
if (user != null) {
setState(() {
_currentUser = user;
});
}
},
icon: Icon(
Icons.refresh_rounded,
color: Color(0xff8705bf),
),
),
],
),

Once the email is verified, the text widget’s color changes from red to green.

Conclusion

You’ll never know how good you can be until you start it. It’s okay to get errors but the awesome part is you get it solved. So, Keep Coding!

Check the project code here.

https://github.com/funmi-cod/fashionapp

❤ ❤ Thanks for staying till the end. ❤❤

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Maria Elijah

Maria Elijah

Flutter developer, IT support specialist and technical writer. I make DIY projects. I love learning as “learning is Life”