Skip to content

Bab 27 — Setup Firebase

Sampai sini aplikasimu berjalan sendirian di satu perangkat. Bagian ini mengubahnya: data tersimpan di awan, pengguna bisa masuk, dan perubahan dari satu perangkat langsung terlihat di perangkat lain.

Firebase adalah pilihan yang sangat umum untuk itu, dan bab ini membahas persiapannya — langkah yang kalau salah akan membuat enam bab berikutnya tidak berjalan.

Apa saja yang ada di Firebase

Firebase bukan satu produk, melainkan sekumpulan layanan. Yang akan kita pakai:

LayananFungsiDibahas di
AuthenticationLogin email, Google, teleponBab 28
Cloud FirestoreBasis data NoSQL waktu nyataBab 29
StorageMenyimpan gambar & berkasBab 29
Cloud MessagingPush notificationBab 30
Cloud FunctionsKode server tanpa mengelola serverBab 30
CrashlyticsLaporan crashBab 38

Arsitekturnya secara garis besar:

   ┌───────────────────────────────────────────────────────┐
   │              APLIKASI FLUTTER                         │
   │                                                       │
   │   firebase_auth   cloud_firestore   firebase_storage  │
   │   firebase_messaging   cloud_functions                │
   └────────────────────────┬──────────────────────────────┘
                            │  SDK (bukan HTTP mentah)
   ┌────────────────────────▼──────────────────────────────┐
   │                  FIREBASE                             │
   │                                                       │
   │  ┌──────────┐  ┌───────────┐  ┌────────┐  ┌────────┐  │
   │  │   Auth   │  │ Firestore │  │Storage │  │  FCM   │  │
   │  └──────────┘  └─────┬─────┘  └────────┘  └────────┘  │
   │                      │                                │
   │            ┌─────────▼─────────┐                      │
   │            │  Security Rules   │  ← gerbang keamanan  │
   │            └───────────────────┘                      │
   └───────────────────────────────────────────────────────┘

Perhatikan Security Rules di bagian bawah. Ini bagian yang paling sering diabaikan pemula dan paling berbahaya kalau salah — kita bahas di Bab 29.

Membuat proyek Firebase

  1. Buka console.firebase.google.com.
  2. Klik Add project, beri nama (misalnya aplikasi-polling).
  3. Google Analytics bersifat opsional — kamu bisa mematikannya untuk proyek belajar.
  4. Tunggu proyek selesai dibuat.

Di dalam proyek, kamu akan melihat daftar layanan di sidebar kiri. Setiap layanan harus diaktifkan satu per satu sebelum bisa dipakai — ini sumber error "permission denied" yang sering membingungkan.

Menghubungkan Flutter dengan FlutterFire CLI

Dulu menghubungkan Flutter ke Firebase berarti mengunduh berkas konfigurasi secara manual untuk Android dan iOS, menaruhnya di folder yang benar, dan mengedit berkas Gradle. Sekarang ada perkakas yang mengurus semuanya.

Memasang perkakas

bash
# Firebase CLI (butuh Node.js)
npm install -g firebase-tools

# Masuk ke akun Google
firebase login

# FlutterFire CLI
dart pub global activate flutterfire_cli

PATH untuk paket Dart global

Kalau perintah flutterfire tidak dikenali setelah dipasang, direktori paket global Dart belum ada di PATH.

Di Windows, tambahkan:

C:\Users\<nama-pengguna>\AppData\Local\Pub\Cache\bin

Di macOS/Linux, tambahkan ke .zshrc atau .bashrc:

bash
export PATH="$PATH":"$HOME/.pub-cache/bin"

Menghubungkan proyek

Dari dalam folder proyek Flutter:

bash
flutterfire configure

Perintah ini akan:

  1. Menampilkan daftar proyek Firebase-mu — pilih yang tadi dibuat.
  2. Menanyakan platform mana yang didukung — pilih Android, iOS, dan lainnya sesuai kebutuhan.
  3. Mendaftarkan aplikasi ke Firebase secara otomatis.
  4. Membuat berkas lib/firebase_options.dart yang berisi seluruh konfigurasi untuk semua platform.
  5. Menaruh google-services.json (Android) dan GoogleService-Info.plist (iOS) di tempat yang benar.

Hasilnya berkas seperti ini:

dart
// lib/firebase_options.dart — DIHASILKAN OTOMATIS, jangan diedit manual
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
    show defaultTargetPlatform, kIsWeb, TargetPlatform;

class DefaultFirebaseOptions {
  static FirebaseOptions get currentPlatform {
    if (kIsWeb) return web;
    switch (defaultTargetPlatform) {
      case TargetPlatform.android:
        return android;
      case TargetPlatform.iOS:
        return ios;
      default:
        throw UnsupportedError('Platform belum dikonfigurasi.');
    }
  }

  static const FirebaseOptions android = FirebaseOptions(
    apiKey: 'AIza...',
    appId: '1:123456789:android:abc123',
    messagingSenderId: '123456789',
    projectId: 'aplikasi-polling',
    storageBucket: 'aplikasi-polling.appspot.com',
  );

  // ... ios, web
}

Apakah kunci ini rahasia?

Berbeda dari kunci API Google Maps di Bab 16, apiKey Firebase tidak rahasia. Ia hanya mengidentifikasi proyekmu, dan memang harus ada di dalam aplikasi.

Yang melindungi datamu adalah Security Rules, bukan kerahasiaan kunci ini. Karena itu firebase_options.dart aman untuk di-commit ke repositori — dan karena itu pula aturan keamanan yang benar menjadi sangat penting.

Memasang paket

yaml
# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter

  # Wajib — fondasi semua layanan Firebase
  firebase_core: ^3.8.0

  # Sesuai kebutuhan
  firebase_auth: ^5.3.3
  cloud_firestore: ^5.5.0
  firebase_storage: ^12.3.6
  firebase_messaging: ^15.1.5
  cloud_functions: ^5.1.5

  # Pendukung
  google_sign_in: ^6.2.2
  image_picker: ^1.1.2
bash
flutter pub get

Versi paket harus cocok

Paket Firebase saling bergantung. Memakai firebase_auth versi lama dengan firebase_core versi baru sering menyebabkan error kompilasi yang tidak jelas.

Kalau kamu mengalaminya, jalankan:

bash
flutter pub upgrade --major-versions

Atau kunjungi firebase.flutter.dev untuk melihat kombinasi versi yang kompatibel.

Inisialisasi di aplikasi

dart
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';

void main() async {
  // Wajib sebelum memanggil kode platform di main()
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  runApp(const AplikasiSaya());
}

Dua baris itu wajib dan urutannya tidak boleh dibalik. Kalau kamu memanggil layanan Firebase apa pun sebelum initializeApp selesai, kamu mendapat:

[core/no-app] No Firebase App '[DEFAULT]' has been created —
call Firebase.initializeApp()

Inisialisasi dengan penanganan kegagalan

Untuk aplikasi produksi, tangani kemungkinan gagalnya:

dart
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  try {
    await Firebase.initializeApp(
      options: DefaultFirebaseOptions.currentPlatform,
    );
    runApp(const AplikasiSaya());
  } catch (kesalahan) {
    runApp(LayarGagalMulai(pesan: kesalahan.toString()));
  }
}

class LayarGagalMulai extends StatelessWidget {
  const LayarGagalMulai({super.key, required this.pesan});

  final String pesan;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Padding(
            padding: const EdgeInsets.all(24),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                const Icon(Icons.cloud_off, size: 64, color: Colors.grey),
                const SizedBox(height: 16),
                const Text(
                  'Gagal terhubung ke server',
                  style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 8),
                const Text(
                  'Periksa koneksi internetmu, lalu buka ulang aplikasi.',
                  textAlign: TextAlign.center,
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Konfigurasi Android

flutterfire configure sudah mengurus sebagian besar. Yang masih perlu kamu periksa ada di android/app/build.gradle:

gradle
android {
    namespace "com.contoh.aplikasi_polling"
    compileSdk 35

    defaultConfig {
        applicationId "com.contoh.aplikasi_polling"

        // Firebase Auth butuh minimal 21
        // Firestore dengan multidex butuh 21 juga
        minSdkVersion 23
        targetSdkVersion 35

        multiDexEnabled true
    }
}

Error "Cannot fit requested classes in a single dex file"

Ini terjadi ketika aplikasimu melewati batas 64 ribu method — sangat mudah dicapai begitu beberapa paket Firebase ditambahkan.

Perbaikannya: multiDexEnabled true seperti di atas, dan tambahkan dependensinya:

gradle
dependencies {
    implementation 'androidx.multidex:multidex:2.0.1'
}

SHA-1 untuk Google Sign-In

Kalau kamu berencana memakai login Google (Bab 28), Firebase butuh sidik jari sertifikat aplikasimu.

bash
# Untuk debug (saat pengembangan)
cd android
./gradlew signingReport

# Di Windows
gradlew signingReport

Salin nilai SHA-1 dan SHA-256 dari varian debug, lalu tempel di Firebase Console → Project Settings → aplikasi Android → Add fingerprint.

Setelah menambahkannya, unduh ulang google-services.json dan ganti yang lama. Ini langkah yang paling sering terlupakan, dan gejalanya adalah login Google yang gagal diam-diam tanpa pesan error yang jelas.

SHA-1 rilis berbeda dari debug

Ketika kamu membangun versi rilis, sertifikatnya berbeda — jadi SHA-1-nya juga berbeda. Kamu harus menambahkan SHA-1 rilis ke Firebase juga, atau login Google akan berfungsi saat pengembangan tetapi gagal di Play Store.

Kalau memakai Play App Signing, ambil SHA-1 dari Play Console → SetupApp integrity.

Konfigurasi iOS

Buka ios/Podfile dan pastikan versi platform minimalnya cukup:

ruby
platform :ios, '13.0'

Lalu pasang dependensinya:

bash
cd ios
pod install
cd ..

Kalau pod install gagal, sering kali karena cache yang usang:

bash
cd ios
rm -rf Pods Podfile.lock
pod repo update
pod install

Bekerja dengan Xcode

Setelah pod install, buka ios/Runner.xcworkspacebukanRunner.xcodeproj. Membuka yang salah membuat Xcode tidak menemukan dependensi CocoaPods, dan pesan errornya sangat tidak membantu.

Struktur data yang akan kita bangun

Sepanjang Bagian IV kita membangun aplikasi polling: pengguna membuat jajak pendapat, orang lain memberi suara, dan hasilnya tampil waktu nyata.

Struktur Firestore-nya:

firestore/
├── users/                          ← koleksi
│   └── {uid}/                      ← dokumen, ID = UID dari Auth
│       ├── nama: "Firnanda"
│       ├── email: "..."
│       ├── urlFoto: "https://..."
│       ├── dibuatPada: Timestamp
│       └── fcmToken: "..."

├── polls/
│   └── {pollId}/
│       ├── judul: "Bahasa favoritmu?"
│       ├── pemilikId: "{uid}"
│       ├── namaPemilik: "Firnanda"
│       ├── opsi: ["Dart", "Kotlin", "Swift"]
│       ├── jumlahSuara: [12, 8, 5]
│       ├── totalSuara: 25
│       ├── urlGambar: "https://..."
│       ├── unggulan: false
│       ├── unggulanSampai: Timestamp?
│       ├── lokasi: GeoPoint?
│       ├── dibuatPada: Timestamp
│       │
│       └── votes/                  ← subkoleksi
│           └── {uid}/
│               ├── indeksOpsi: 0
│               └── padaWaktu: Timestamp

└── chats/
    └── {uid}/
        └── messages/
            └── {messageId}/
                ├── teks: "..."
                ├── dariAi: false
                └── padaWaktu: Timestamp

Beberapa keputusan desain yang layak diperhatikan sekarang, karena akan dijelaskan lebih dalam nanti:

ID dokumen users sama dengan UID dari Authentication. Ini membuat aturan keamanan menjadi sangat sederhana: request.auth.uid == userId.

jumlahSuara disimpan sebagai array di dokumen poll. Ini denormalisasi — data yang sebenarnya bisa dihitung dari subkoleksi votes, tetapi disimpan langsung agar menampilkan hasil tidak butuh membaca ratusan dokumen.

Subkoleksi votes memakai UID sebagai ID dokumen. Dengan begitu, satu pengguna secara struktural tidak mungkin memberi suara dua kali — bukan karena kita memeriksanya, tetapi karena tidak ada tempat untuk suara keduanya.

Firestore dibahas tuntas di Bab 29.

Struktur proyek

lib/
├── main.dart
├── firebase_options.dart          ← dihasilkan flutterfire

├── data/
│   ├── models/
│   │   ├── pengguna.dart
│   │   ├── poll.dart
│   │   └── pesan.dart
│   ├── services/
│   │   ├── layanan_auth.dart      ← pembungkus FirebaseAuth
│   │   ├── layanan_poll.dart      ← pembungkus Firestore
│   │   ├── layanan_storage.dart   ← pembungkus Storage
│   │   └── layanan_notifikasi.dart← pembungkus FCM
│   └── repositories/
│       ├── auth_repository.dart
│       └── poll_repository.dart

├── providers/                     ← atau logic/ kalau memakai BLoC
│   ├── penyedia_auth.dart
│   └── penyedia_poll.dart

└── presentation/
    ├── screens/
    └── widgets/

Bungkus SDK Firebase, jangan panggil langsung dari UI

Godaan untuk menulis FirebaseFirestore.instance.collection('polls') langsung di dalam widget itu besar — dan itu kesalahan yang akan menyulitkanmu nanti.

Bungkus di kelas layanan seperti struktur di atas. Alasannya sama dengan yang dibahas di Bab 22: kamu bisa mengujinya dengan mock, mengganti implementasinya, dan menemukan semua kode Firebase di satu tempat ketika API-nya berubah.

Emulator lokal

Ini perkakas yang sangat berguna dan jarang dipakai pemula. Firebase Emulator Suite menjalankan Auth, Firestore, Storage, dan Functions di komputermu, sehingga kamu bisa:

  • Bereksperimen tanpa mengotori data produksi.
  • Bekerja tanpa koneksi internet.
  • Menguji aturan keamanan dengan cepat.
  • Menghindari biaya kuota saat pengembangan.
bash
# Sekali di folder proyek
firebase init emulators

# Menjalankannya
firebase emulators:start

Arahkan aplikasi ke emulator saat mode debug:

dart
// lib/main.dart
import 'package:flutter/foundation.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';

const _pakaiEmulator = bool.fromEnvironment('USE_EMULATOR');

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  if (kDebugMode && _pakaiEmulator) {
    // 10.0.2.2 adalah alamat host dari emulator Android
    const host = '10.0.2.2';

    await FirebaseAuth.instance.useAuthEmulator(host, 9099);
    FirebaseFirestore.instance.useFirestoreEmulator(host, 8080);
    await FirebaseStorage.instance.useStorageEmulator(host, 9199);
  }

  runApp(const AplikasiSaya());
}

Menjalankannya:

bash
flutter run --dart-define=USE_EMULATOR=true

Alamat host yang benar

  • Emulator Android: 10.0.2.2 — alamat khusus yang menunjuk ke komputer host.
  • Simulator iOS: localhost.
  • Perangkat fisik: alamat IP komputermu di jaringan lokal, misalnya 192.168.1.5.

Memeriksa penggunaan dan biaya

Firebase punya paket gratis (Spark) yang cukup murah hati, tetapi ada batasnya. Yang paling mudah terlampaui:

LayananBatas gratis harian
Firestore baca50.000 dokumen
Firestore tulis20.000 dokumen
Firestore hapus20.000 dokumen
Storage unduh1 GB
Cloud Functions125.000 pemanggilan/bulan

Kesalahan yang menghabiskan kuota

Angka 50.000 pembacaan terdengar banyak, tetapi mudah habis kalau kamu:

  • Memakai snapshots() pada koleksi besar tanpa limit() — setiap perubahan membaca ulang seluruh dokumen yang cocok.
  • Membaca data di dalam build() tanpa cache.
  • Membuat listener baru setiap kali widget dibangun ulang.

Aktifkan Budget alerts di Google Cloud Console sejak hari pertama, meskipun kamu masih di paket gratis. Cloud Functions dan beberapa layanan lain butuh paket berbayar (Blaze), dan tagihan yang tak terduga bukan pengalaman yang menyenangkan.

Daftar periksa sebelum lanjut

Sebelum masuk ke bab berikutnya, pastikan semua ini beres:

  • Proyek Firebase dibuat di console
  • firebase-tools dan flutterfire_cli terpasang
  • flutterfire configure dijalankan → lib/firebase_options.dart ada
  • firebase_core ada di pubspec.yaml
  • Firebase.initializeApp() dipanggil di main() yang async
  • WidgetsFlutterBinding.ensureInitialized() dipanggil SEBELUMNYA
  • minSdkVersion minimal 23 di android/app/build.gradle
  • multiDexEnabled true
  • platform :ios, '13.0' di ios/Podfile
  • SHA-1 debug ditambahkan (kalau akan memakai Google Sign-In)
  • google-services.json diunduh ULANG setelah menambah SHA-1
  • Aplikasi berhasil dijalankan tanpa error Firebase

Uji cepat bahwa semuanya tersambung:

dart
// Tambahkan sementara di initState layar pertama
import 'package:firebase_core/firebase_core.dart';

@override
void initState() {
  super.initState();
  final app = Firebase.app();
  debugPrint('✅ Firebase terhubung: ${app.options.projectId}');
}

Kalau projectId tercetak dengan benar, kamu siap melanjutkan.

Ikhtisar

  • Firebase adalah kumpulan layanan, dan setiap layanan harus diaktifkan satu per satu di console sebelum bisa dipakai.
  • flutterfire configure mengurus seluruh konfigurasi platform dan menghasilkan lib/firebase_options.dart — jangan mengedit berkas itu manual.
  • apiKey Firebase tidak rahasia. Yang melindungi datamu adalah Security Rules, bukan kerahasiaan kunci.
  • Urutan di main() wajib: WidgetsFlutterBinding.ensureInitialized()await Firebase.initializeApp()runApp().
  • Android butuh minSdkVersion minimal 23 dan multiDexEnabled true.
  • iOS butuh platform :ios, '13.0' dan pod install. Buka Runner.xcworkspace, bukan Runner.xcodeproj.
  • Untuk Google Sign-In, tambahkan SHA-1 ke Firebase lalu unduh ulang google-services.json — langkah kedua ini paling sering terlupakan.
  • SHA-1 rilis berbeda dari debug — tambahkan keduanya, atau login akan gagal di Play Store.
  • Bungkus SDK Firebase di kelas layanan, jangan panggil langsung dari widget.
  • Firebase Emulator Suite memungkinkan pengembangan tanpa internet, tanpa mengotori data produksi, dan tanpa memakai kuota.
  • Aktifkan budget alerts sejak hari pertama. snapshots() tanpa limit() pada koleksi besar adalah cara tercepat menghabiskan kuota baca.

Berikutnya: Bab 28 — Firebase Authentication.

Transkrip asli

Disintesis dari 4_flutter_ai-chatbot-n-firebase/1_getting-started-with-flutter-and-firebase.md (4 video: arsitektur Flutter & Firebase, instalasi, membuat proyek dan menghubungkan Firebase) dan 1_flutter_core/13_push-notification.md (video 2, 6–7: App & Firebase Setup, Firebase CLI & SDK Setup). Lihat PDF Firebase & AI.

Rangkuman pembelajaran pribadi, disusun ulang dari beberapa kursus Flutter.