Flutter Firebase Phone Authentication Tutorial: Step-by-Step Guide
Phone authentication is an essential feature for many apps today, and with Flutter and Firebase, implementing it becomes seamless. In this tutorial, we will walk through setting up Firebase Phone Authentication in a Flutter app. You will learn how to create screens for inputting a phone number, verifying it via OTP (One Time Password), and redirecting users to a home screen if the OTP is valid.
Watch Video Tutorial Here:
We’ll add form validation to ensure that the phone number and OTP entered are correct, giving users an intuitive and error-free experience. Let’s get started!
Prerequisites
Before we dive into the code, ensure you have the following:
- Flutter SDK installed on your machine.
- Firebase Project set up in the Firebase Console
- Basic knowledge of Flutter and Firebase.
Step 1: Set Up Firebase for Flutter
1. Create a Firebase Project:
- Head over to the Firebase Console and create a new project.
- Follow the steps to register your Android and/or iOS app with Firebase. You will need to download the `google-services.json` (for Android) or `GoogleService-Info.plist` (for iOS) and configure it in your Flutter project.
2. Enable Phone Authentication:
- In the Firebase Console, navigate to Authentication > Sign-in method and enable Phone as a sign-in option.
3. Add Firebase SDK:
Add the Firebase and Firebase Auth dependencies to your `pubspec.yaml` file:
dependencies:
flutter:
sdk: flutter
firebase_core: latest_version
firebase_auth: latest_version
Run `flutter pub get` to install these dependencies.
Step 2: Initialize Firebase in Your Flutter Project
In the `main.dart` file, initialize Firebase before running the app. This ensures Firebase is properly set up before authentication processes start.
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MaterialApp(
home: PhoneAuthScreen(),
));
}
Step 3: Create the Phone Number Input Screen
First, we need a screen where the user enters their phone number. This will trigger Firebase to send an OTP to the provided phone number.
class PhoneAuthScreen extends StatefulWidget {
@override
_PhoneAuthScreenState createState() => _PhoneAuthScreenState();
}
class _PhoneAuthScreenState extends State<PhoneAuthScreen> {
final _phoneController = TextEditingController();
final _formKey = GlobalKey<FormState>();
String? _verificationId;
void _sendCodeToPhoneNumber() async {
if (_formKey.currentState!.validate()) {
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: _phoneController.text.trim(),
verificationCompleted: (PhoneAuthCredential credential) {
// Auto-retrieval or instant verification
},
verificationFailed: (FirebaseAuthException e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Verification failed: ${e.message}')),
);
},
codeSent: (String verificationId, int? resendToken) {
_verificationId = verificationId;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OtpScreen(verificationId: verificationId),
),
);
},
codeAutoRetrievalTimeout: (String verificationId) {
_verificationId = verificationId;
},
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Phone Authentication')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
controller: _phoneController,
decoration: InputDecoration(labelText: 'Phone Number'),
keyboardType: TextInputType.phone,
validator: (value) {
if (value == null || value.isEmpty || !RegExp(r'^\+\d{1,3}\d{9,10}$').hasMatch(value)) {
return 'Please enter a valid phone number with country code';
}
return null;
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _sendCodeToPhoneNumber,
child: Text('Send OTP'),
),
],
),
),
),
);
}
}
Step 4: Create the OTP Input Screen
Once the OTP has been sent to the user’s phone, they will need to input it. The OTP is then verified against Firebase, and if valid, the user is signed in.
class OtpScreen extends StatefulWidget {
final String verificationId;
OtpScreen({required this.verificationId});
@override
_OtpScreenState createState() => _OtpScreenState();
}
class _OtpScreenState extends State<OtpScreen> {
final _otpController = TextEditingController();
final _formKey = GlobalKey<FormState>();
void _verifyOtp() async {
if (_formKey.currentState!.validate()) {
try {
final credential = PhoneAuthProvider.credential(
verificationId: widget.verificationId,
smsCode: _otpController.text.trim(),
);
await FirebaseAuth.instance.signInWithCredential(credential);
// Redirect to home if OTP is valid
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
);
} on FirebaseAuthException catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to sign in: ${e.message}')),
);
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Enter OTP')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
controller: _otpController,
decoration: InputDecoration(labelText: 'OTP'),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty || value.length != 6) {
return 'Please enter a valid 6-digit OTP';
}
return null;
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _verifyOtp,
child: Text('Verify OTP'),
),
],
),
),
),
);
}
}
Step 5: Home Screen
After a successful OTP verification, the user is redirected to the home screen.
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Center(
child: Text('Welcome Home!'),
),
);
}
}
Step 6: Validations
We’ve added the following validation rules to improve user experience:
- Phone Number: Ensures the phone number includes a valid country code and matches the expected format.
- OTP: Validates that the OTP contains exactly 6 digits.
Error messages will be shown to the user via a `SnackBar` for easy feedback.
Step 7: Testing Your App
To test phone authentication, you’ll need to use a real device, as phone authentication doesn’t work on emulators without special setup. You can also configure **test phone numbers** in the Firebase console under **Authentication > Phone Sign-In > Test phone numbers** for testing in emulators.
Conclusion
With just a few lines of code, you’ve implemented Firebase Phone Authentication in Flutter, complete with validation and error handling. This basic setup can be extended with more features like profile setup after login or linking accounts for existing users. With Firebase handling the heavy lifting, integrating authentication into your app has never been easier!
If you have any questions or want to dive deeper into more advanced topics, feel free to drop them in the comments section below!
Happy coding! 🚀
#flutter #phoneauth #fluttertutorial #flutterphoneauth #firebase