Flutter
Firebase Authentication untuk Pemula
Tutorial Firebase Authentication di Flutter: setup firebase_auth, register, login, logout, current user, authStateChanges, AuthService, AuthRepository, dan redirect login/home.
Firebase Authentication untuk Pemula
Firebase Authentication adalah layanan Firebase untuk mengelola login user. Dengan Firebase Auth, aplikasi bisa memiliki fitur register, login, logout, current user, dan mendeteksi apakah user sedang login atau belum.
Materi ini fokus pada email/password authentication.
Yang akan dipelajari:
- konsep Firebase Auth
- enable Email/Password di Firebase Console
- install package
firebase_auth - register user
- login user
- logout
- membaca current user
- mendengarkan auth state
- membuat
AuthService - membuat
AuthRepository - membuat halaman login dan register sederhana
- redirect login/home
- error umum
1. Apa Itu Authentication
Authentication adalah proses memastikan siapa user yang sedang memakai aplikasi.
Contoh:
User membuka aplikasi
-> user login dengan email/password
-> Firebase memverifikasi akun
-> aplikasi mendapatkan user
-> aplikasi menampilkan halaman home
Jika user belum login:
currentUser == null
Jika user sudah login:
currentUser != null
Authentication penting sebelum aplikasi punya data per user. Misalnya Task Manager online:
User A hanya melihat task milik User A
User B hanya melihat task milik User B
2. Alur Firebase Auth
Alur register:
User isi email dan password
-> aplikasi memanggil createUserWithEmailAndPassword
-> Firebase membuat akun
-> user otomatis login
-> aplikasi masuk ke Home
Alur login:
User isi email dan password
-> aplikasi memanggil signInWithEmailAndPassword
-> Firebase memverifikasi akun
-> jika berhasil, user login
-> aplikasi masuk ke Home
Alur logout:
User tekan logout
-> aplikasi memanggil signOut
-> user keluar
-> aplikasi kembali ke Login
Alur auth state:
authStateChanges
-> null berarti belum login
-> User berarti sudah login
3. Persiapan Firebase Project
Sebelum kode Flutter, pastikan:
- Project Flutter sudah terhubung ke Firebase.
- File
firebase_options.dartsudah ada. - Firebase sudah diinisialisasi di
main.dart. - Email/Password provider sudah diaktifkan di Firebase Console.
Jika belum, ikuti materi:
Di Firebase Console:
Authentication
-> Sign-in method
-> Email/Password
-> Enable
-> Save
Jika Email/Password belum diaktifkan, register/login akan gagal.
4. Install Package
Tambahkan package:
flutter pub add firebase_core
flutter pub add firebase_auth
Lalu:
flutter pub get
Import:
import 'package:firebase_auth/firebase_auth.dart';
5. Inisialisasi Firebase
Contoh main.dart:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';
import 'app.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
Penjelasan:
WidgetsFlutterBinding.ensureInitialized()wajib sebelum kode async Firebase.Firebase.initializeApp()menghubungkan app ke Firebase.DefaultFirebaseOptions.currentPlatformberasal darifirebase_options.dart.
6. Register User
Firebase Auth menyediakan:
createUserWithEmailAndPassword
Contoh:
Future<UserCredential> register({
required String email,
required String password,
}) async {
return FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email,
password: password,
);
}
Dengan error handling:
Future<UserCredential> register({
required String email,
required String password,
}) async {
try {
return await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (error) {
if (error.code == 'weak-password') {
throw Exception('Password terlalu lemah');
}
if (error.code == 'email-already-in-use') {
throw Exception('Email sudah terdaftar');
}
throw Exception(error.message ?? 'Gagal register');
}
}
Error umum saat register:
weak-password: password terlalu lemahemail-already-in-use: email sudah dipakaiinvalid-email: format email tidak valid
7. Login User
Firebase Auth menyediakan:
signInWithEmailAndPassword
Contoh:
Future<UserCredential> login({
required String email,
required String password,
}) async {
return FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
}
Dengan error handling:
Future<UserCredential> login({
required String email,
required String password,
}) async {
try {
return await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (error) {
if (error.code == 'user-not-found') {
throw Exception('User tidak ditemukan');
}
if (error.code == 'wrong-password') {
throw Exception('Password salah');
}
if (error.code == 'invalid-email') {
throw Exception('Format email tidak valid');
}
throw Exception(error.message ?? 'Gagal login');
}
}
Catatan:
Beberapa kode error bisa berubah tergantung konfigurasi Firebase, versi package, dan proteksi keamanan. Selalu tampilkan pesan yang aman dan mudah dipahami user.
8. Logout
Logout:
Future<void> logout() async {
await FirebaseAuth.instance.signOut();
}
Setelah logout, authStateChanges() akan mengirim null.
9. Current User
Membaca user saat ini:
final user = FirebaseAuth.instance.currentUser;
Contoh:
if (user == null) {
print('Belum login');
} else {
print('Sudah login: ${user.email}');
}
Data yang sering dipakai:
user.uid
user.email
user.displayName
user.photoURL
Paling penting:
user.uid
uid dipakai sebagai identitas unik user. Nanti saat memakai Firestore, uid bisa dipakai untuk menyimpan task per user.
10. Auth State Changes
Untuk mendeteksi user login/logout, gunakan:
FirebaseAuth.instance.authStateChanges()
Contoh:
Stream<User?> authStateChanges() {
return FirebaseAuth.instance.authStateChanges();
}
Di UI:
StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
final user = snapshot.data;
if (user == null) {
return const LoginPage();
}
return const HomePage();
},
)
Penjelasan:
- Jika
user == null, tampilkan Login. - Jika
user != null, tampilkan Home. - Saat login/logout, UI otomatis berubah.
11. Membuat AuthService
Buat file:
lib/services/auth_service.dart
Isi:
import 'package:firebase_auth/firebase_auth.dart';
class AuthService {
final FirebaseAuth _auth;
AuthService({
FirebaseAuth? auth,
}) : _auth = auth ?? FirebaseAuth.instance;
User? get currentUser => _auth.currentUser;
Stream<User?> authStateChanges() {
return _auth.authStateChanges();
}
Future<UserCredential> register({
required String email,
required String password,
}) async {
return _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
}
Future<UserCredential> login({
required String email,
required String password,
}) async {
return _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
}
Future<void> logout() async {
await _auth.signOut();
}
Future<void> sendPasswordResetEmail(String email) async {
await _auth.sendPasswordResetEmail(email: email);
}
}
Kenapa dibuat service?
Supaya detail Firebase Auth tidak ditulis langsung di UI.
12. Membuat AuthRepository
Buat file:
lib/repositories/auth_repository.dart
Isi:
import 'package:firebase_auth/firebase_auth.dart';
import '../services/auth_service.dart';
class AuthRepository {
final AuthService _authService;
AuthRepository({
AuthService? authService,
}) : _authService = authService ?? AuthService();
User? get currentUser => _authService.currentUser;
Stream<User?> authStateChanges() {
return _authService.authStateChanges();
}
Future<void> register({
required String email,
required String password,
}) async {
try {
await _authService.register(
email: email,
password: password,
);
} on FirebaseAuthException catch (error) {
throw Exception(_mapAuthError(error));
}
}
Future<void> login({
required String email,
required String password,
}) async {
try {
await _authService.login(
email: email,
password: password,
);
} on FirebaseAuthException catch (error) {
throw Exception(_mapAuthError(error));
}
}
Future<void> logout() async {
await _authService.logout();
}
Future<void> sendPasswordResetEmail(String email) async {
await _authService.sendPasswordResetEmail(email);
}
String _mapAuthError(FirebaseAuthException error) {
switch (error.code) {
case 'invalid-email':
return 'Format email tidak valid';
case 'weak-password':
return 'Password terlalu lemah';
case 'email-already-in-use':
return 'Email sudah terdaftar';
case 'user-not-found':
return 'User tidak ditemukan';
case 'wrong-password':
return 'Password salah';
case 'network-request-failed':
return 'Koneksi internet bermasalah';
default:
return error.message ?? 'Terjadi kesalahan authentication';
}
}
}
Kenapa perlu repository?
Karena repository menjadi API yang dipakai aplikasi. Jika nanti login pindah ke backend custom, UI/state tidak harus tahu detailnya.
13. Struktur Folder
Struktur sederhana:
lib/
main.dart
app.dart
services/
auth_service.dart
repositories/
auth_repository.dart
pages/
auth_gate.dart
login_page.dart
register_page.dart
home_page.dart
Jika memakai state management:
lib/
providers/
auth_provider.dart
atau:
lib/
cubits/
auth_cubit.dart
auth_state.dart
Untuk materi ini, kita mulai dengan StreamBuilder agar lebih mudah dipahami.
14. Membuat AuthGate
AuthGate menentukan halaman awal berdasarkan status login.
Buat file:
lib/pages/auth_gate.dart
Isi:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import '../repositories/auth_repository.dart';
import 'home_page.dart';
import 'login_page.dart';
class AuthGate extends StatelessWidget {
AuthGate({super.key});
final authRepository = AuthRepository();
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: authRepository.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
final user = snapshot.data;
if (user == null) {
return const LoginPage();
}
return const HomePage();
},
);
}
}
Penjelasan:
authStateChanges()mendeteksi login/logout.- Jika user null, tampilkan
LoginPage. - Jika user ada, tampilkan
HomePage.
15. Pasang AuthGate di main.dart
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Firebase Auth App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: AuthGate(),
);
}
}
Sekarang aplikasi akan otomatis memilih Login atau Home.
16. Membuat LoginPage
Buat file:
lib/pages/login_page.dart
Isi:
import 'package:flutter/material.dart';
import '../repositories/auth_repository.dart';
import 'register_page.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final authRepository = AuthRepository();
final emailController = TextEditingController();
final passwordController = TextEditingController();
bool isLoading = false;
@override
void dispose() {
emailController.dispose();
passwordController.dispose();
super.dispose();
}
Future<void> login() async {
final email = emailController.text.trim();
final password = passwordController.text.trim();
if (email.isEmpty || password.isEmpty) {
showMessage('Email dan password wajib diisi');
return;
}
setState(() {
isLoading = true;
});
try {
await authRepository.login(
email: email,
password: password,
);
} catch (error) {
showMessage(error.toString());
} finally {
if (mounted) {
setState(() {
isLoading = false;
});
}
}
}
void showMessage(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Login'),
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
TextField(
controller: emailController,
keyboardType: TextInputType.emailAddress,
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 12),
TextField(
controller: passwordController,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
FilledButton(
onPressed: isLoading ? null : login,
child: Text(isLoading ? 'Loading...' : 'Login'),
),
const SizedBox(height: 8),
TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RegisterPage(),
),
);
},
child: const Text('Belum punya akun? Register'),
),
],
),
);
}
}
Setelah login berhasil, tidak perlu manual navigate ke Home karena AuthGate akan menerima auth state baru dan otomatis menampilkan HomePage.
17. Membuat RegisterPage
Buat file:
lib/pages/register_page.dart
Isi:
import 'package:flutter/material.dart';
import '../repositories/auth_repository.dart';
class RegisterPage extends StatefulWidget {
const RegisterPage({super.key});
@override
State<RegisterPage> createState() => _RegisterPageState();
}
class _RegisterPageState extends State<RegisterPage> {
final authRepository = AuthRepository();
final emailController = TextEditingController();
final passwordController = TextEditingController();
final confirmPasswordController = TextEditingController();
bool isLoading = false;
@override
void dispose() {
emailController.dispose();
passwordController.dispose();
confirmPasswordController.dispose();
super.dispose();
}
Future<void> register() async {
final email = emailController.text.trim();
final password = passwordController.text.trim();
final confirmPassword = confirmPasswordController.text.trim();
if (email.isEmpty || password.isEmpty || confirmPassword.isEmpty) {
showMessage('Semua field wajib diisi');
return;
}
if (password != confirmPassword) {
showMessage('Konfirmasi password tidak sama');
return;
}
setState(() {
isLoading = true;
});
try {
await authRepository.register(
email: email,
password: password,
);
if (mounted) {
Navigator.pop(context);
}
} catch (error) {
showMessage(error.toString());
} finally {
if (mounted) {
setState(() {
isLoading = false;
});
}
}
}
void showMessage(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Register'),
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
TextField(
controller: emailController,
keyboardType: TextInputType.emailAddress,
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 12),
TextField(
controller: passwordController,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 12),
TextField(
controller: confirmPasswordController,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Konfirmasi Password',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
FilledButton(
onPressed: isLoading ? null : register,
child: Text(isLoading ? 'Loading...' : 'Register'),
),
],
),
);
}
}
Catatan:
Register dengan Firebase biasanya otomatis membuat user login. Jika app memakai AuthGate, setelah register berhasil halaman bisa masuk ke Home.
18. Membuat HomePage
Buat file:
lib/pages/home_page.dart
Isi:
import 'package:flutter/material.dart';
import '../repositories/auth_repository.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
final authRepository = AuthRepository();
final user = authRepository.currentUser;
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
actions: [
IconButton(
onPressed: () async {
await authRepository.logout();
},
icon: const Icon(Icons.logout),
),
],
),
body: Center(
child: Text('Login sebagai: ${user?.email ?? '-'}'),
),
);
}
}
Setelah logout, AuthGate akan membaca user null dan menampilkan LoginPage.
19. Password Reset
Tambahkan di AuthRepository sudah ada:
Future<void> sendPasswordResetEmail(String email) async {
await _authService.sendPasswordResetEmail(email);
}
Contoh pemakaian:
Future<void> forgotPassword() async {
final email = emailController.text.trim();
if (email.isEmpty) {
showMessage('Isi email terlebih dahulu');
return;
}
try {
await authRepository.sendPasswordResetEmail(email);
showMessage('Link reset password dikirim ke email');
} catch (error) {
showMessage(error.toString());
}
}
Button:
TextButton(
onPressed: forgotPassword,
child: const Text('Lupa password?'),
)
20. Validasi Form
Validasi minimal:
bool isValidEmail(String email) {
return email.contains('@') && email.contains('.');
}
Contoh:
if (!isValidEmail(email)) {
showMessage('Format email tidak valid');
return;
}
if (password.length < 6) {
showMessage('Password minimal 6 karakter');
return;
}
Untuk aplikasi production, validasi bisa dibuat lebih rapi memakai Form, TextFormField, dan validator.
21. Task per User
Setelah user login, kita bisa memakai uid.
final uid = FirebaseAuth.instance.currentUser?.uid;
Nanti saat memakai Firestore, struktur data bisa seperti:
users/{uid}/tasks/{taskId}
Artinya:
- setiap user punya collection task sendiri
- user A tidak bercampur dengan user B
- security rules bisa dibuat berdasarkan
request.auth.uid
Ini alasan kenapa Firebase Auth sebaiknya dipelajari sebelum Firestore CRUD.
22. Error Umum
Email/Password belum diaktifkan
Solusi:
Firebase Console
-> Authentication
-> Sign-in method
-> Enable Email/Password
Firebase belum diinisialisasi
Pastikan:
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
dipanggil sebelum runApp.
firebase_options.dart tidak ditemukan
Jalankan:
flutterfire configure
Password terlalu lemah
Firebase biasanya membutuhkan password minimal 6 karakter untuk email/password.
currentUser null padahal baru buka app
Gunakan authStateChanges() untuk mendengarkan status auth. Jangan hanya mengandalkan currentUser terlalu awal saat aplikasi baru start.
23. Checklist Firebase Auth
Pastikan sudah selesai:
- Firebase project sudah dibuat.
- Flutter sudah terhubung ke Firebase.
-
firebase_options.dartsudah ada. -
firebase_coresudah dipasang. -
firebase_authsudah dipasang. - Email/Password provider sudah diaktifkan.
- Firebase sudah diinisialisasi di
main.dart. - Bisa register user.
- Bisa login user.
- Bisa logout.
- Bisa membaca current user.
- Bisa memakai
authStateChanges. - Ada
AuthService. - Ada
AuthRepository. - Ada
AuthGate. - Login berhasil masuk Home.
- Logout kembali ke Login.
Jika checklist ini aman, kamu siap masuk Cloud Firestore CRUD.
24. Lanjutan Setelah Ini
Materi berikutnya:
- Cloud Firestore Dasar.
- Firestore CRUD.
- Task Manager online per user.
- Security Rules dasar.
- Firebase Storage untuk upload file/gambar.
Urutan yang disarankan:
Firebase Auth
-> Firestore CRUD
-> Task per user
-> Security Rules