In Flutter app development, implementing user authentication features such as sign-in and sign-up is crucial for creating personalized and secure user experiences. Firebase Authentication offers a comprehensive solution for handling user authentication with ease. This blog post guides you through using Firebase Authentication in Flutter to implement user sign-in and sign-up features, complete with detailed code examples and explanations.
What is Firebase Authentication?
Firebase Authentication provides backend services, SDKs, and UI libraries to authenticate users to your app. It supports authentication using passwords, phone numbers, popular federated identity providers like Google, Facebook, and Twitter, and more. It’s easy to integrate into a Flutter application, offering a robust and secure authentication mechanism.
Why Use Firebase Authentication?
- Ease of Use: Firebase simplifies the implementation of complex authentication flows.
- Multi-Platform Support: Seamless integration across Android, iOS, and web applications.
- Secure: Firebase handles the secure storage and management of user credentials.
- Variety of Providers: Supports multiple authentication methods including email/password, social media logins, and phone authentication.
Prerequisites
Before you begin, ensure you have:
- A Firebase project created in the Firebase Console.
- A Flutter project set up.
- Firebase configured for your Flutter project (including
firebase_coreandfirebase_authpackages).
Step-by-Step Implementation
Let’s walk through the implementation of user sign-in and sign-up using Firebase Authentication in a Flutter app.
Step 1: Add Firebase Dependencies
First, add the necessary Firebase packages to your pubspec.yaml file:
dependencies:
flutter:
sdk: flutter
firebase_core: ^2.15.0 # Ensure you use the latest version
firebase_auth: ^4.6.0 # Ensure you use the latest version
Run flutter pub get to install these dependencies.
Step 2: Initialize Firebase
In your main.dart file, initialize Firebase before running the app:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Firebase Authentication Demo',
home: AuthenticationWrapper(),
);
}
}
class AuthenticationWrapper extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Add logic to check if user is signed in, navigate accordingly
return Scaffold(
appBar: AppBar(title: Text('Authentication Demo')),
body: Center(child: Text('Authentication Screen')),
);
}
}
Step 3: Implement Sign-Up Functionality
Create a method to handle user sign-up using Firebase:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// Sign up with email and password
Future signUpWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
return result.user;
} catch (e) {
print(e.toString());
return null;
}
}
}
Step 4: Implement Sign-In Functionality
Create a method to handle user sign-in using Firebase:
// Sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
return result.user;
} catch (e) {
print(e.toString());
return null;
}
}
Step 5: Build the UI for Sign-Up and Sign-In
Create a simple UI for sign-up and sign-in, utilizing the AuthService:
class SignUpScreen extends StatefulWidget {
@override
_SignUpScreenState createState() => _SignUpScreenState();
}
class _SignUpScreenState extends State {
final AuthService _auth = AuthService();
final _formKey = GlobalKey();
String email = '';
String password = '';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Sign Up')),
body: Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 50.0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(height: 20.0),
TextFormField(
decoration: InputDecoration(hintText: 'Email'),
validator: (val) => val!.isEmpty ? 'Enter an email' : null,
onChanged: (val) {
setState(() => email = val);
},
),
SizedBox(height: 20.0),
TextFormField(
decoration: InputDecoration(hintText: 'Password'),
obscureText: true,
validator: (val) => val!.length < 6 ? 'Enter a password 6+ characters long' : null,
onChanged: (val) {
setState(() => password = val);
},
),
SizedBox(height: 20.0),
ElevatedButton(
child: Text(
'Sign Up',
style: TextStyle(color: Colors.white),
),
onPressed: () async {
if (_formKey.currentState!.validate()) {
dynamic result = await _auth.signUpWithEmailAndPassword(email, password);
if (result == null) {
print('Error signing up');
} else {
print('Signed up');
print(result.uid);
}
}
},
),
],
),
),
),
);
}
}
class SignInScreen extends StatefulWidget {
@override
_SignInScreenState createState() => _SignInScreenState();
}
class _SignInScreenState extends State<SignInScreen> {
final AuthService _auth = AuthService();
final _formKey = GlobalKey<FormState>();
String email = '';
String password = '';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Sign In')),
body: Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 50.0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(height: 20.0),
TextFormField(
decoration: InputDecoration(hintText: 'Email'),
validator: (val) => val!.isEmpty ? 'Enter an email' : null,
onChanged: (val) {
setState(() => email = val);
},
),
SizedBox(height: 20.0),
TextFormField(
decoration: InputDecoration(hintText: 'Password'),
obscureText: true,
validator: (val) => val!.length < 6 ? 'Enter a password 6+ characters long' : null,
onChanged: (val) {
setState(() => password = val);
},
),
SizedBox(height: 20.0),
ElevatedButton(
child: Text(
'Sign In',
style: TextStyle(color: Colors.white),
),
onPressed: () async {
if (_formKey.currentState!.validate()) {
dynamic result = await _auth.signInWithEmailAndPassword(email, password);
if (result == null) {
print('Error signing in');
} else {
print('Signed in');
print(result.uid);
}
}
},
),
],
),
),
),
);
}
}
Step 6: Handle User Authentication State
Check and manage the user’s authentication state to redirect them to the appropriate screen (e.g., home screen if signed in, sign-in screen if signed out). You can use StreamBuilder to listen for changes in the authentication state:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class AuthenticationWrapper extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
// User is signed in
return HomeScreen();
} else {
// User is signed out
return SignInScreen();
}
},
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Center(
child: Text('Welcome! You are signed in.'),
),
);
}
}
Handling Errors
It’s important to handle potential errors during sign-up and sign-in, such as invalid email formats, weak passwords, or network issues. Displaying user-friendly error messages improves the user experience.
// Example of handling Firebase Authentication exceptions
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
return result.user;
} 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.');
}
return null;
} catch (e) {
print(e.toString());
return null;
}
Conclusion
Using Firebase Authentication simplifies the process of implementing secure user sign-in and sign-up features in Flutter apps. By following the steps outlined in this blog post and incorporating the provided code examples, you can easily integrate robust authentication functionality, enhance user security, and improve the overall user experience. Firebase Authentication offers a versatile and reliable solution for managing user credentials in your Flutter projects.