Adding the authentication logic

We'll place the authentication logic in a new class. This class will contain four methods:

  • A method to log in
  • A method to sign up
  • A method to log out
  • A method to retrieve the current user

Let's begin adding our login, as follows: 

  1. Create a new folder in the lib folder of our app and call it shared. Inside the shared folder, let's also create a new file called authentication.dart.
  2. In this file, we'll import the firebase_auth package and async.dart.
  3. Next, we'll create a new class called Authentication, as follows:
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
class Authentication {}
  1. In the Authentication class, we'll declare an instance of FirebaseAuth, which is the object that enables the use of Firebase Authentication's methods and properties, as follows:
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
All methods in FirebaseAuth are asynchronous.
  1. Then, we'll create a method that will allow our users to log in. It will be asynchronous, will return a Future of type String, and will take two strings, one for the username and one for the password, as follows:
Future<String> login(String email, String password) async {}
  1. Inside the login() method, we just need to call the signInWithEmailAndPassword() method, which does exactly what its name implies, as follows:
AuthResult authResult = await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password
);
  1. Next, let's create a FirebaseUser object. The FirebaseUser represents a user and has several properties that can be used by the app, such as uid, which is the user ID, and email. In this case, the function returns the user's uid, as shown in the following snippet:
FirebaseUser user = authResult.user;
return user.uid;
For a full list of properties for the FirebaseUser class, have a look at the documentation page at https://pub.dev/documentation/firebase_auth/latest/firebase_auth/FirebaseUser-class.html.
  1. The signup process is very similar. We'll create an asynchronous method called signUp() and, instead of calling the signInWithUserNameAndPassword() method, we will call the createUserWithUserNameAndPassword() method, which will create a new user in our Firebase project. The code for this method is shown in the following snippet:
Future<String> signUp(String email, String password) async {
AuthResult authResult = await
_firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password
);
FirebaseUser user = authResult.user;
return user.uid;
}
  1. We now need to create a method that will sign out the logged-in user. This is extremely simple, as we'll only need to call the signOut() method on the FirebaseAuth instance, as follows:
  Future<void> signOut() async {
return _firebaseAuth.signOut();
}
  1. The last method we'll add to the Authentication class is a method that retrieves the current user. This will be useful when we need to check whether a user is logged in or not. We'll call it getUser() and it will call the FirebaseAuth.currentUser() method, as illustrated in the following code snippet:
Future<FirebaseUser> getUser() async {
FirebaseUser user = await _firebaseAuth.currentUser();
return user;
}

Now that we have completed the authentication logic, we need to add the methods of the Authentication class in the authentication screen. Also, when the user enters the app for the first time, they should see the authentication screen. Once logged in, the user should see the event screen.

In order to make this possible, we can use the getUser() method to check whether a CurrentUser is available or not. We'll do that from a new screen, called LaunchScreen. This screen's task will be to show a loading animation while the user data is retrieved. It will then redirect the user to the appropriate screen—that is, either the authentication screen or the event screen.

Let's create the launch screen for our app, as follows:

  1. Let's create a new file in the screens folder, called launch_screen.dart. At the top of the file, we'll import material.dart, the two screens that may be opened by the launch screen, the firebase_auth package, and the cloud_firestore package.
  2. Next, we'll create a stateful widget called LaunchScreen. In the build() method of the _launchScreenState class, we'll show a CircularProgressIndicator widget, which shows a nice animation while data is loading, as follows:
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'login_screen.dart';
import 'event_screen.dart';

class LaunchScreen extends StatefulWidget {
@override
_LaunchScreenState createState() => _LaunchScreenState();
}

class _LaunchScreenState extends State<LaunchScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: CircularProgressIndicator(),),

);
}
}
  1. Next, we will override the InitState() method, which checks the current user status.
  2. Inside the method, we'll call an instance of the Authentication class and then call the getUser() method.
  3. If there is a logged-in user available, we'll show the user the EventScreen; otherwise, we'll just show the Login screen.
  4. Notice that, instead of using the push() method on the navigator, we're using pushReplacement(). This not only pushes the new route to the top of the screen, but it also removes the previous route. This prevents the user from navigating to the LaunchScreen. This is illustrated in the following code block:
@override
void initState() {
super.initState();
Authentication auth = Authentication();
auth.getUser().then((user) {
MaterialPageRoute route;
if (user != null) {
route = MaterialPageRoute(builder: (context) =>
EventScreen());
}
else {
route = MaterialPageRoute(builder: (context) =>
LoginScreen());
}
Navigator.pushReplacement(context, route);
}).catchError((err)=> print(err));
}

In order to try this out, we'll need to change what happens when the user opens the app for the first time. Currently, as it opens, the app shows the EventScreen, but we want to change that. This is because, if the user is already logged in, they will see the EventScreen; otherwise, we need to show the LoginScreen. Before trying the LaunchScreen, we need to call it from the MyApp class in the main.dart file. So, let's change the home of the MaterialApp,so it calls LaunchScreen, as follows:

home: LaunchScreen(),

If you try the app right now, after a very short CircularProgressIndicator animation (depending on your device, it might be so fast you won't even see it), you should see the Login screen.

Unfortunately, from there, we can neither log in nor sign in yet, but we are very close. Let's get back to the loginscreen.dart file and add the logic to perform the login and signup tasks, as follows:

  1. First, we'll import the authentication.dart file that contains the calls to the Firebase Authentication service, as follows:
import '../shared/authentication.dart';
  1. Then, we'll create an Authentication variable called auth, override the initState() method, and create an instance of the Authentication class, as follows:
Authentication auth;
@override
void initState() {
auth = Authentication();
super.initState();
}
  1. In the submit() method in the _LoginScreenState class, which is called when the user presses the main button, we will reset the _message so that, if there was a previous validation message, it's removed from the screen. We'll also make the method asynchronous, as follows:
Future submit() async {
setState(() {
_message = "";
});
  1. Then, in a try-catch block, we'll call the login or signup methods of the auth object, based on the value of the _isLogin Boolean variable. After each action, we'll also print information about the logged-in or signed-up user in the Debug console, as follows:
try {
if (_isLogin) {
_userId = await auth.login(txtEmail.text,
txtPassword.text);
print('Login for user $_userId');
}
else
{
_userId = await auth.signUp(txtEmail.text,
txtPassword.text);
print('Sign up for user $_userId');
}
if (_userId != null) {
Navigator.push(context, MaterialPageRoute(builder:
(context)=> EventScreen()));
}
} catch (e) {
print('Error: $e');
setState(() {
_message = e.message;
});
}

If the login or signup tasks were successful, the _userId should now contain the ID of the logged-in user. However, if the username or password is wrong, or the call wasn't successful, the Firebase Authentication call should fail, and in this case, we'll print an error message so that the user will be able to see the problem.

In order to try the login procedure, I suggest you try to fail the login a few times, by using a badly formatted email, skipping the password, or entering a wrong email and password, just to see the different messages that the Firebase Authentication service returns when something goes wrong.

After trying the different error messages, you can sign up with your correct data; if the process is successful, you should be redirected to the event screen. Now, if you restart the app, you should skip the login process and be redirected to the event screen immediately, as the app is keeping your login data.

  1. We need to add a way for the user to log out. We'll use an IconButton widget in the actions of the AppBar of the Scaffold in the EventScreen class to call the logout method of our Authentication class, as shown in the following code block:
final Authentication auth = new Authentication();
return Scaffold(
appBar: AppBar(
title: Text('Event'),
actions:[
IconButton(
icon: Icon(Icons.exit_to_app),
onPressed: () {
auth.signOut().then((result) {
Navigator.push(context,
MaterialPageRoute(builder: (context) =>
LoginScreen()));
});
},
)
],
),

If you try the app now and press the logout IconButton, you should be redirected to the login screen again;  if you log in, you will be taken back to the EventScreen. So now in our app, only logged-in users can access event information.

But the security we've implemented here is only client-side, and any security expert would tell us that this means almost no security at all. The good news is that we can very easily set up server-side security in Firebase. Let's see how, next!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.60.158