Kenali Firebase untuk Flutter

Tetap teratur dengan koleksi Simpan dan kategorikan konten berdasarkan preferensi Anda.

1. Sebelum Anda mulai

Dalam codelab ini, Anda mempelajari beberapa dasar Firebase untuk membuat aplikasi seluler Flutter untuk Android dan iOS.

Prasyarat

Apa yang akan Anda pelajari

  • Cara membuat RSVP acara dan aplikasi obrolan buku tamu di Android, iOS, Web, dan macOS dengan Flutter.
  • Cara mengautentikasi pengguna dengan Firebase Authentication dan menyinkronkan data dengan Firestore.

Apa yang Anda butuhkan

Salah satu perangkat berikut:

  • Perangkat Android atau iOS fisik yang terhubung ke komputer Anda dan disetel ke mode pengembang.
  • Simulator iOS (Membutuhkan alat Xcode ).
  • Emulator Android (Memerlukan penyiapan di Android Studio ).

Anda juga membutuhkan yang berikut ini:

  • Browser pilihan Anda, seperti Google Chrome.
  • IDE atau editor teks pilihan Anda yang dikonfigurasi dengan plugin Dart dan Flutter, seperti Android Studio atau Visual Studio Code .
  • Flutter atau beta versi stable terbaru jika Anda senang hidup di edge.
  • Akun Google untuk pembuatan dan pengelolaan proyek Firebase Anda.
  • Firebase CLI masuk ke Akun Google Anda.

2. Dapatkan kode contoh

Unduh versi awal proyek Anda dari GitHub:

  1. Dari baris perintah, tiru repositori GitHub di direktori flutter-codelabs :
git clone https://github.com/flutter/codelabs.git flutter-codelabs

Direktori flutter-codelabs berisi kode untuk kumpulan codelabs. Kode untuk codelab ini ada di direktori flutter-codelabs/firebase-get-to-know-flutter . Direktori tersebut berisi serangkaian snapshot yang menunjukkan bagaimana seharusnya proyek Anda terlihat di akhir setiap langkah. Misalnya, Anda berada di langkah kedua.

  1. Temukan file yang cocok untuk langkah kedua:
cd flutter-codelabs/firebase-get-to-know-flutter/step_02

Jika Anda ingin melompat ke depan atau melihat bagaimana sesuatu seharusnya terlihat setelah satu langkah, lihat di direktori yang dinamai menurut langkah yang Anda minati.

Impor aplikasi awal

  • Buka atau impor flutter-codelabs/firebase-get-to-know-flutter/step_02 di IDE pilihan Anda. Direktori ini berisi kode awal untuk codelab, yang terdiri dari aplikasi pertemuan Flutter yang belum berfungsi.

Temukan file yang perlu dikerjakan

Kode dalam aplikasi ini tersebar di beberapa direktori. Pemisahan fungsionalitas ini membuat pekerjaan lebih mudah karena mengelompokkan kode berdasarkan fungsionalitas.

  • Temukan file berikut:
    • lib/main.dart : File ini berisi titik masuk utama dan widget aplikasi.
    • lib/src/widgets.dart : File ini berisi beberapa widget untuk membantu membakukan gaya aplikasi. Mereka menyusun layar aplikasi pemula.
    • lib/src/authentication.dart : File ini berisi implementasi parsial Authentication dengan serangkaian widget untuk membuat pengalaman pengguna login untuk autentikasi berbasis email Firebase. Widget untuk alur autentikasi ini belum digunakan di aplikasi awal, tetapi Anda akan segera menambahkannya.

Anda menambahkan file tambahan yang diperlukan untuk membuat aplikasi lainnya.

Tinjau file lib/main.dart

Aplikasi ini memanfaatkan paket google_fonts untuk menjadikan Roboto font default di seluruh aplikasi. Anda dapat menjelajahi fonts.google.com dan menggunakan font yang Anda temukan di berbagai bagian aplikasi.

Anda menggunakan widget pembantu dari file lib/src/widgets.dart dalam bentuk Header , Paragraph dan IconAndDetail . Widget ini menghilangkan kode duplikat untuk mengurangi kekacauan dalam tata letak halaman yang dijelaskan di HomePage . Ini juga memungkinkan tampilan dan nuansa yang konsisten.

Inilah tampilan aplikasi Anda di Android, iOS, Web, dan macOS:

3. Buat dan konfigurasikan proyek Firebase

Tampilan informasi acara sangat bagus untuk tamu Anda, tetapi tidak terlalu berguna untuk siapa pun. Anda perlu menambahkan beberapa fungsi dinamis ke aplikasi. Untuk melakukannya, Anda perlu menghubungkan Firebase ke aplikasi Anda. Untuk memulai Firebase, Anda perlu membuat dan mengonfigurasi proyek Firebase.

Buat proyek Firebase

  1. Masuk ke Firebase .
  2. Di konsol, klik Tambahkan Proyek atau Buat proyek .
  3. Di kolom Nama proyek , masukkan Firebase-Flutter-Codelab , lalu klik Lanjutkan .

4395e4e67c08043a.png

  1. Klik opsi pembuatan proyek. Jika diminta, terima persyaratan Firebase, tetapi lewati penyiapan Google Analytics karena Anda tidak akan menggunakannya untuk aplikasi ini.

b7138cde5f2c7b61.png

Untuk mempelajari lebih lanjut tentang proyek Firebase, lihat Memahami proyek Firebase .

Aplikasi menggunakan produk Firebase berikut, yang tersedia untuk aplikasi web:

  • Autentikasi: Memungkinkan pengguna masuk ke aplikasi Anda.
  • Firestore: Menyimpan data terstruktur di cloud dan mendapatkan notifikasi instan saat data berubah.
  • Aturan Keamanan Firebase: Mengamankan database Anda.

Beberapa produk ini memerlukan konfigurasi khusus atau Anda perlu mengaktifkannya di konsol Firebase.

Aktifkan autentikasi masuk email

  1. Di panel ikhtisar Proyek konsol Firebase, perluas menu Bangun .
  2. Klik Autentikasi > Memulai > Metode masuk > Email/Kata Sandi > Aktifkan > Simpan .

58e3e3e23c2f16a4.png

Aktifkan Firestore

Aplikasi web menggunakan Firestore untuk menyimpan pesan obrolan dan menerima pesan obrolan baru.

Aktifkan Firestore:

  • Di menu Bangun , klik Cloud Firestore > Buat database .

99e8429832d23fa3.png

  1. Pilih Mulai dalam mode pengujian , lalu baca penafian tentang aturan keamanan. Mode uji memastikan bahwa Anda dapat dengan bebas menulis ke database selama pengembangan.

6be00e26c72ea032.png

  1. Klik Berikutnya , lalu pilih lokasi untuk database Anda. Anda dapat menggunakan default. Anda tidak dapat mengubah lokasi nanti.

278656eefcfb0216.png

  1. Klik Aktifkan .

4. Konfigurasikan Firebase

Untuk menggunakan Firebase dengan Flutter, Anda harus menyelesaikan tugas berikut untuk mengonfigurasi project Flutter agar menggunakan library FlutterFire dengan benar:

  1. Tambahkan dependensi FlutterFire ke proyek Anda.
  2. Daftarkan platform yang diinginkan di proyek Firebase.
  3. Unduh file konfigurasi khusus platform lalu tambahkan ke kode.

Di direktori level atas aplikasi Flutter Anda, terdapat subdirektori android , ios , macos , dan web , yang masing-masing menyimpan file konfigurasi khusus platform untuk iOS dan Android.

Konfigurasikan dependensi

Anda perlu menambahkan library FlutterFire untuk dua produk Firebase yang Anda gunakan di aplikasi ini: Authentication dan Firestore.

  • Dari baris perintah, tambahkan dependensi berikut:
$ flutter pub add firebase_core

Paket firebase_core adalah kode umum yang diperlukan untuk semua plugin Flutter Firebase.

$ flutter pub add firebase_auth

Paket firebase_auth memungkinkan integrasi dengan Otentikasi.

$ flutter pub add cloud_firestore

Paket cloud_firestore memungkinkan akses ke penyimpanan data Firestore.

$ flutter pub add provider

Paket firebase_ui_auth menyediakan sekumpulan widget dan utilitas untuk meningkatkan kecepatan developer dengan alur autentikasi.

$ flutter pub add firebase_ui_auth

Anda menambahkan paket yang diperlukan, tetapi Anda juga perlu mengonfigurasi proyek runner iOS, Android, macOS, dan Web untuk menggunakan Firebase dengan tepat. Anda juga menggunakan paket provider yang memungkinkan pemisahan logika bisnis dari logika tampilan.

Instal FlutterFire CLI

FlutterFire CLI bergantung pada Firebase CLI yang mendasarinya.

  1. Jika Anda belum melakukannya, instal Firebase CLI di komputer Anda.
  2. Instal FlutterFire CLI:
$ dart pub global activate flutterfire_cli

Setelah diinstal, perintah flutterfire tersedia secara global.

Konfigurasikan aplikasi Anda

CLI mengekstrak informasi dari proyek Firebase Anda dan aplikasi proyek yang dipilih untuk menghasilkan semua konfigurasi untuk platform tertentu.

Di root aplikasi Anda, jalankan perintah configure :

$ flutterfire configure

Perintah konfigurasi memandu Anda melalui proses berikut:

  1. Pilih proyek Firebase berdasarkan file .firebaserc atau dari Firebase Console.
  2. Tentukan platform untuk konfigurasi, seperti Android, iOS, macOS, dan web.
  3. Identifikasi aplikasi Firebase untuk mengekstrak konfigurasi. Secara default, CLI mencoba mencocokkan aplikasi Firebase secara otomatis berdasarkan konfigurasi project Anda saat ini.
  4. Hasilkan file firebase_options.dart di proyek Anda.

Konfigurasikan macOS

Flutter di macOS membuat aplikasi dengan kotak pasir sepenuhnya. Karena aplikasi ini terintegrasi dengan jaringan untuk berkomunikasi dengan server Firebase, Anda perlu mengonfigurasi aplikasi Anda dengan hak istimewa klien jaringan.

macos/Runner/DebugProfile.entitlements

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.security.app-sandbox</key>
	<true/>
	<key>com.apple.security.cs.allow-jit</key>
	<true/>
	<key>com.apple.security.network.server</key>
	<true/>
  <!-- Add the following two lines -->
	<key>com.apple.security.network.client</key>
	<true/>
</dict>
</plist>

macos/Runner/Release.entitlements

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.security.app-sandbox</key>
	<true/>
  <!-- Add the following two lines -->
	<key>com.apple.security.network.client</key>
	<true/>
</dict>
</plist>

Untuk informasi selengkapnya, lihat Dukungan desktop untuk Flutter .

5. Tambahkan fungsionalitas RSVP

Sekarang setelah Anda menambahkan Firebase ke aplikasi, Anda dapat membuat tombol RSVP yang mendaftarkan orang dengan Authentication . Untuk Android native, iOS native, dan Web, ada paket FirebaseUI Auth , tetapi Anda perlu membangun kemampuan ini untuk Flutter.

Proyek yang Anda ambil sebelumnya menyertakan sekumpulan widget yang mengimplementasikan antarmuka pengguna untuk sebagian besar alur autentikasi. Anda menerapkan logika bisnis untuk mengintegrasikan Otentikasi dengan aplikasi.

Tambahkan logika bisnis dengan paket Provider

Gunakan paket provider untuk membuat objek status aplikasi terpusat tersedia di seluruh pohon aplikasi widget Flutter:

  1. Ubah impor di bagian atas file lib/main.dart :

lib/main.dart

import 'dart:async';                                     // new
import 'package:firebase_auth/firebase_auth.dart'        // new
    hide EmailAuthProvider, PhoneAuthProvider;           // new
import 'package:firebase_core/firebase_core.dart';       // new
import 'package:firebase_ui_auth/firebase_ui_auth.dart'; // new
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';                 // new

import 'firebase_options.dart';                          // new
import 'src/authentication.dart';                        // new
import 'src/widgets.dart';

Pernyataan import memperkenalkan Firebase Core dan Auth, menarik paket provider yang membuat objek status aplikasi tersedia di seluruh pohon widget, dan menyertakan widget autentikasi dari paket firebase_ui_auth .

Objek status aplikasi ApplicationState ini memiliki satu tanggung jawab utama untuk langkah ini, yaitu mengingatkan pohon widget bahwa ada pembaruan ke status yang diautentikasi.

  1. Tambahkan kelas berikut ke akhir file lib/main.dart :

lib/main.dart

class ApplicationState extends ChangeNotifier {
  ApplicationState() {
    init();
  }

  bool _loggedIn = false;
  bool get loggedIn => _loggedIn;

  Future<void> init() async {
    await Firebase.initializeApp(
        options: DefaultFirebaseOptions.currentPlatform);

    FirebaseUIAuth.configureProviders([
      EmailAuthProvider(),
    ]);

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loggedIn = true;
      } else {
        _loggedIn = false;
      }
      notifyListeners();
    });
  }
}

Anda hanya menggunakan penyedia untuk mengomunikasikan status login pengguna ke aplikasi. Untuk mengizinkan pengguna masuk, gunakan UI yang disediakan oleh paket firebase_ui_auth , yang merupakan cara bagus untuk mem-bootstrap layar masuk dengan cepat di aplikasi Anda.

Integrasikan aliran autentikasi

  1. Hubungkan status aplikasi dengan inisialisasi aplikasi, lalu tambahkan alur autentikasi ke HomePage :

lib/main.dart

void main() {
  // Modify from here...
  WidgetsFlutterBinding.ensureInitialized();

  runApp(ChangeNotifierProvider(
    create: (context) => ApplicationState(),
    builder: ((context, child) => const App()),
  ));
  // ...to here.
}

Modifikasi fungsi main() membuat paket penyedia bertanggung jawab untuk membuat instance objek status aplikasi dengan widget ChangeNotifierProvider . Anda menggunakan kelas provider khusus ini karena objek status aplikasi memperluas kelas ChangeNotifier , yang memungkinkan paket provider mengetahui kapan harus menampilkan kembali widget yang bergantung.

  1. Perbarui aplikasi Anda untuk menangani navigasi ke berbagai layar yang disediakan FirebaseUI untuk Anda:

lib/main.dart

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      //Start adding here
      initialRoute: '/home',
      routes: {
        '/home': (context) {
          return const HomePage();
        },
        '/sign-in': ((context) {
          return SignInScreen(
            actions: [
              ForgotPasswordAction(((context, email) {
                Navigator.of(context)
                    .pushNamed('/forgot-password', arguments: {'email': email});
              })),
              AuthStateChangeAction(((context, state) {
                if (state is SignedIn || state is UserCreated) {
                  var user = (state is SignedIn)
                      ? state.user
                      : (state as UserCreated).credential.user;
                  if (user == null) {
                    return;
                  }
                  if (state is UserCreated) {
                    user.updateDisplayName(user.email!.split('@')[0]);
                  }
                  if (!user.emailVerified) {
                    user.sendEmailVerification();
                    const snackBar = SnackBar(
                        content: Text(
                            'Please check your email to verify your email address'));
                    ScaffoldMessenger.of(context).showSnackBar(snackBar);
                  }
                  Navigator.of(context).pushReplacementNamed('/home');
                }
              })),
            ],
          );
        }),
        '/forgot-password': ((context) {
          final arguments = ModalRoute.of(context)?.settings.arguments
              as Map<String, dynamic>?;

          return ForgotPasswordScreen(
            email: arguments?['email'] as String,
            headerMaxExtent: 200,
          );
        }),
        '/profile': ((context) {
          return ProfileScreen(
            providers: [],
            actions: [
              SignedOutAction(
                ((context) {
                  Navigator.of(context).pushReplacementNamed('/home');
                }),
              ),
            ],
          );
        })
      },
      // end adding here
      title: 'Firebase Meetup',
      theme: ThemeData(
        buttonTheme: Theme.of(context).buttonTheme.copyWith(
              highlightColor: Colors.deepPurple,
            ),
        primarySwatch: Colors.deepPurple,
        textTheme: GoogleFonts.robotoTextTheme(
          Theme.of(context).textTheme,
        ),
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
    );
  }
}

Setiap layar memiliki jenis tindakan berbeda yang terkait dengannya berdasarkan status baru alur autentikasi. Setelah sebagian besar perubahan status dalam autentikasi, Anda dapat mengalihkan rute kembali ke layar yang diinginkan, apakah itu layar beranda atau layar lain, seperti profil.

  1. Dalam metode build kelas HomePage , integrasikan status aplikasi dengan widget AuthFunc :

lib/main.dart

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Consumer<ApplicationState>(
        builder: (context, appState, child) => Scaffold(
              appBar: AppBar(
                title: const Text('Firebase Meetup'),
              ),
              body: ListView(
                children: <Widget>[
                  Image.asset('assets/codelab.png'),
                  const SizedBox(height: 8),
                  const IconAndDetail(Icons.calendar_today, 'October 30'),
                  const IconAndDetail(Icons.location_city, 'San Francisco'),
                  // Add from here
                  Consumer<ApplicationState>(
                    builder: (context, appState, _) => AuthFunc(
                        loggedIn: appState.loggedIn,
                        signOut: () {
                          FirebaseAuth.instance.signOut();
                        }),
                  ),
                  // to here
                  const Divider(
                    height: 8,
                    thickness: 1,
                    indent: 8,
                    endIndent: 8,
                    color: Colors.grey,
                  ),
                  const Header("What we'll be doing"),
                  const Paragraph(
                    'Join us for a day full of Firebase Workshops and Pizza!',
                  ),
                ],
              ),
            ));
  }
}

Anda membuat instance widget AuthFunc dan membungkusnya dalam widget Consumer . Widget Konsumen adalah cara yang biasa digunakan oleh paket provider untuk membangun kembali bagian pohon ketika status aplikasi berubah. Widget AuthFunc adalah widget pelengkap yang Anda uji.

Uji aliran autentikasi

cdf2d25e436bd48d.png

  1. Di aplikasi, ketuk tombol RSVP untuk memulai SignInScreen .

2a2cd6d69d172369.png

  1. Masukan alamat email. Jika Anda sudah terdaftar, sistem akan meminta Anda memasukkan kata sandi. Jika tidak, sistem akan meminta Anda untuk melengkapi formulir pendaftaran.

e5e65065dba36b54.png

  1. Masukkan kata sandi yang kurang dari enam karakter untuk memeriksa alur penanganan kesalahan. Jika Anda terdaftar, Anda akan melihat kata sandinya.
  2. Masukkan kata sandi yang salah untuk memeriksa alur penanganan kesalahan.
  3. Masukkan kata sandi yang benar. Anda melihat pengalaman masuk, yang menawarkan kepada pengguna kemampuan untuk keluar.

4ed811a25b0cf816.png

6. Tulis pesan ke Firestore

Sangat menyenangkan mengetahui bahwa pengguna akan datang, tetapi Anda harus memberi tamu hal lain untuk dilakukan di aplikasi. Bagaimana jika mereka bisa meninggalkan pesan di buku tamu? Mereka dapat berbagi mengapa mereka bersemangat untuk datang atau siapa yang ingin mereka temui.

Untuk menyimpan pesan obrolan yang ditulis pengguna di aplikasi, Anda menggunakan Firestore .

Model data

Firestore adalah database NoSQL, dan data yang disimpan dalam database dibagi menjadi koleksi, dokumen, kolom, dan subkoleksi. Anda menyimpan setiap pesan obrolan sebagai dokumen dalam koleksi gustbook , yang merupakan koleksi tingkat atas.

7c20dc8424bb1d84.png

Tambahkan pesan ke Firestore

Di bagian ini, Anda menambahkan fungsionalitas bagi pengguna untuk menulis pesan ke database. Pertama, Anda menambahkan bidang formulir dan tombol kirim, lalu Anda menambahkan kode yang menghubungkan elemen-elemen ini dengan database.

  1. Di file lib/main.dart , tambahkan impor untuk paket cloud_firestore dan dart:async :

lib/main.dart

import 'dart:async';                                    // new

import 'package:cloud_firestore/cloud_firestore.dart';  // new
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';

import 'firebase_options.dart';
import 'src/authentication.dart';
import 'src/widgets.dart';
  1. Di akhir file lib/main.dart , tambahkan widget stateful GuestBook Tamu untuk membuat elemen UI bidang pesan dan tombol kirim:

lib/main.dart

class GuestBook extends StatefulWidget {
  const GuestBook({required this.addMessage, super.key});
  final FutureOr<void> Function(String message) addMessage;

  @override
  State<GuestBook> createState() => _GuestBookState();
}

class _GuestBookState extends State<GuestBook> {
  final _formKey = GlobalKey<FormState>(debugLabel: '_GuestBookState');
  final _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Form(
        key: _formKey,
        child: Row(
          children: [
            Expanded(
              child: TextFormField(
                controller: _controller,
                decoration: const InputDecoration(
                  hintText: 'Leave a message',
                ),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Enter your message to continue';
                  }
                  return null;
                },
              ),
            ),
            const SizedBox(width: 8),
            StyledButton(
              onPressed: () async {
                if (_formKey.currentState!.validate()) {
                  await widget.addMessage(_controller.text);
                  _controller.clear();
                }
              },
              child: Row(
                children: const [
                  Icon(Icons.send),
                  SizedBox(width: 4),
                  Text('SEND'),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Ada beberapa tempat menarik di sini. Pertama, Anda membuat instance formulir sehingga Anda dapat memvalidasi bahwa pesan tersebut benar-benar berisi konten dan menampilkan pesan kesalahan kepada pengguna jika tidak ada. Untuk memvalidasi formulir, Anda mengakses status formulir di belakang formulir dengan GlobalKey . Untuk informasi selengkapnya tentang Tombol dan cara menggunakannya, lihat Kapan Menggunakan Tombol .

Perhatikan juga cara widget ditata, Anda memiliki Row dengan TextFormField dan StyledButton , yang berisi Row . Perhatikan juga TextFormField dibungkus dengan widget yang Expanded , yang memaksa TextFormField untuk mengisi ruang ekstra di baris. Untuk lebih memahami mengapa hal ini diperlukan, lihat Memahami batasan .

Sekarang Anda memiliki widget yang memungkinkan pengguna memasukkan beberapa teks untuk ditambahkan ke Buku Tamu, Anda perlu menampilkannya di layar.

  1. Edit badan HomePage untuk menambahkan dua baris berikut di akhir anak-anak ListView :
const Header("What we'll be doing"),
const Paragraph(
  'Join us for a day full of Firebase Workshops and Pizza!',
),
// Add the following two lines.
const Header('Discussion'),
GuestBook(addMessage: (message) => print(message)),

Meskipun cukup untuk menampilkan widget, ini tidak cukup untuk melakukan sesuatu yang bermanfaat. Anda segera memperbarui kode ini agar berfungsi.

Pratinjau aplikasi

Saat pengguna mengklik SEND , ini memicu cuplikan kode berikut. Itu menambahkan konten bidang input pesan ke koleksi guestbook database. Secara khusus, metode addMessageToGuestBook menambahkan konten pesan ke dokumen baru dengan ID yang dibuat secara otomatis di koleksi guestbook .

Perhatikan bahwa FirebaseAuth.instance.currentUser.uid adalah referensi ke ID unik yang dibuat secara otomatis yang diberikan Authentication untuk semua pengguna yang masuk.

  • Di file lib/main.dart , tambahkan metode addMessageToGuestBook . Anda menghubungkan kemampuan ini dengan antarmuka pengguna di langkah berikutnya.

lib/main.dart

class ApplicationState extends ChangeNotifier {

  // Current content of ApplicationState elided ...

  // Add from here...
  Future<DocumentReference> addMessageToGuestBook(String message) {
    if (!_loggedIn) {
      throw Exception('Must be logged in');
    }

    return FirebaseFirestore.instance
        .collection('guestbook')
        .add(<String, dynamic>{
      'text': message,
      'timestamp': DateTime.now().millisecondsSinceEpoch,
      'name': FirebaseAuth.instance.currentUser!.displayName,
      'userId': FirebaseAuth.instance.currentUser!.uid,
    });
  }
  // ...to here.
}

Hubungkan UI dan basis data

Anda memiliki UI tempat pengguna dapat memasukkan teks yang ingin mereka tambahkan ke Buku Tamu dan Anda memiliki kode untuk menambahkan entri ke Firestore. Sekarang yang perlu Anda lakukan hanyalah menghubungkan keduanya.

  • Di file lib/main.dart , lakukan perubahan berikut pada widget HomePage :

lib/main.dart

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Firebase Meetup'),
      ),
      body: ListView(
        children: <Widget>[
          Image.asset('assets/codelab.png'),
          const SizedBox(height: 8),
          const IconAndDetail(Icons.calendar_today, 'October 30'),
          const IconAndDetail(Icons.location_city, 'San Francisco'),
          Consumer<ApplicationState>(
            builder: (context, appState, _) => AuthFunc(
                loggedIn: appState.loggedIn,
                signOut: () {
                  FirebaseAuth.instance.signOut();
                }),
          ),
          const Divider(
            height: 8,
            thickness: 1,
            indent: 8,
            endIndent: 8,
            color: Colors.grey,
          ),
          const Header("What we'll be doing"),
          const Paragraph(
            'Join us for a day full of Firebase Workshops and Pizza!',
          ),
          // Modify from here...
          Consumer<ApplicationState>(
            builder: (context, appState, _) => Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                if (appState.loggedIn) ...[
                  const Header('Discussion'),
                  GuestBook(
                    addMessage: (message) =>
                        appState.addMessageToGuestBook(message),
                  ),
                ],
              ],
            ),
          ),
          // ...to here.
        ],
      ),
    );
  }
}

Anda mengganti dua baris yang Anda tambahkan di awal langkah ini dengan implementasi penuh. Anda kembali menggunakan Consumer<ApplicationState> untuk membuat status aplikasi tersedia untuk bagian pohon yang Anda render. Ini memungkinkan Anda bereaksi terhadap seseorang yang memasukkan pesan di UI dan menerbitkannya di database. Di bagian berikutnya, Anda menguji apakah pesan yang ditambahkan dipublikasikan di database.

Uji pengiriman pesan

  1. Jika perlu, masuk ke aplikasi.
  2. Masukkan pesan, seperti Hey there! , lalu klik KIRIM .

Tindakan ini menulis pesan ke database Firestore Anda. Namun, Anda tidak melihat pesan tersebut di aplikasi Flutter Anda yang sebenarnya karena Anda masih perlu mengimplementasikan pengambilan data, yang Anda lakukan di langkah berikutnya. Namun, di dasbor Basis Data konsol Firebase, Anda dapat melihat pesan tambahan Anda di koleksi guestbook . Jika Anda mengirim lebih banyak pesan, Anda menambahkan lebih banyak dokumen ke koleksi guestbook Anda. Misalnya, lihat cuplikan kode berikut:

713870af0b3b63c.png

7. Baca pesan

Sangat menyenangkan bahwa tamu dapat menulis pesan ke database, tetapi mereka belum dapat melihatnya di aplikasi. Saatnya memperbaikinya!

Sinkronkan pesan

Untuk menampilkan pesan, Anda perlu menambahkan pemroses yang memicu saat data berubah, lalu membuat elemen UI yang menampilkan pesan baru. Anda menambahkan kode ke status aplikasi yang mendengarkan pesan yang baru ditambahkan dari aplikasi.

  1. Di file lib/main.dart sebelum widget Buku GuestBook , tambahkan kelas berikut untuk menampilkan tampilan terstruktur dari data yang Anda simpan di Firestore.

lib/main.dart

class GuestBookMessage {
  GuestBookMessage({required this.name, required this.message});
  final String name;
  final String message;
}
  1. Di bagian ApplicationState tempat Anda menentukan status dan getter, tambahkan baris berikut:

lib/main.dart

  bool _loggedIn = false;
  bool get loggedIn => _loggedIn;

  // Add from here...
  StreamSubscription<QuerySnapshot>? _guestBookSubscription;
  List<GuestBookMessage> _guestBookMessages = [];
  List<GuestBookMessage> get guestBookMessages => _guestBookMessages;
  // ...to here.
  1. Di bagian inisialisasi ApplicationState , tambahkan baris berikut untuk berlangganan kueri pada kumpulan dokumen saat pengguna masuk dan berhenti berlangganan saat mereka keluar:

lib/main.dart

  Future<void> init() async {
    await Firebase.initializeApp(
        options: DefaultFirebaseOptions.currentPlatform);

    FirebaseUIAuth.configureProviders([
      EmailAuthProvider(),
    ]);
    
    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loggedIn = true;
        _guestBookSubscription = FirebaseFirestore.instance
            .collection('guestbook')
            .orderBy('timestamp', descending: true)
            .snapshots()
            .listen((snapshot) {
          _guestBookMessages = [];
          for (final document in snapshot.docs) {
            _guestBookMessages.add(
              GuestBookMessage(
                name: document.data()['name'] as String,
                message: document.data()['text'] as String,
              ),
            );
          }
          notifyListeners();
        });
      } else {
        _loggedIn = false;
        _guestBookMessages = [];
        _guestBookSubscription?.cancel();
      }
      notifyListeners();
    });
  }

Bagian ini penting karena di situlah Anda membuat kueri atas koleksi guestbook , dan menangani langganan dan berhenti berlangganan koleksi ini. Anda mendengarkan streaming, tempat Anda merekonstruksi cache lokal dari pesan di koleksi guestbook dan juga menyimpan referensi ke langganan ini sehingga Anda dapat berhenti berlangganan nanti. Ada banyak hal yang terjadi di sini, jadi Anda harus menjelajahinya dalam debugger untuk memeriksa apa yang terjadi untuk mendapatkan model mental yang lebih jelas. Untuk informasi selengkapnya, lihat Dapatkan pembaruan waktu nyata dengan Firestore .

  1. Di widget GuestBook , tambahkan daftar pesan sebagai bagian dari konfigurasi untuk menghubungkan status perubahan ini ke antarmuka pengguna:

lib/main.dart

class GuestBook extends StatefulWidget {
  // Modify the following line:
  const GuestBook({super.key, required this.addMessage, required this.messages,});
  final FutureOr<void> Function(String message) addMessage;
  final List<GuestBookMessage> messages; // new

  @override
  _GuestBookState createState() => _GuestBookState();
}
  1. Di _GuestBookState , ubah metode build sebagai berikut untuk mengekspos konfigurasi ini:

lib/main.dart

class _GuestBookState extends State<GuestBook> {
  final _formKey = GlobalKey<FormState>(debugLabel: '_GuestBookState');
  final _controller = TextEditingController();

  @override
  // Modify from here...
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        // ...to here.
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: Form(
            key: _formKey,
            child: Row(
              children: [
                Expanded(
                  child: TextFormField(
                    controller: _controller,
                    decoration: const InputDecoration(
                      hintText: 'Leave a message',
                    ),
                    validator: (value) {
                      if (value == null || value.isEmpty) {
                        return 'Enter your message to continue';
                      }
                      return null;
                    },
                  ),
                ),
                const SizedBox(width: 8),
                StyledButton(
                  onPressed: () async {
                    if (_formKey.currentState!.validate()) {
                      await widget.addMessage(_controller.text);
                      _controller.clear();
                    }
                  },
                  child: Row(
                    children: const [
                      Icon(Icons.send),
                      SizedBox(width: 4),
                      Text('SEND'),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
        // Modify from here...
        const SizedBox(height: 8),
        for (var message in widget.messages)
          Paragraph('${message.name}: ${message.message}'),
        const SizedBox(height: 8),
      ],
      // ...to here.
    );
  }
}

Anda membungkus konten sebelumnya dari metode build() dengan widget Column dan kemudian Anda menambahkan koleksi for di bagian belakang anak-anak Column untuk menghasilkan Paragraph baru untuk setiap pesan dalam daftar pesan.

  1. Perbarui isi HomePage untuk membuat GuestBook dengan benar menggunakan parameter messages baru:

lib/main.dart

Consumer<ApplicationState>(
  builder: (context, appState, _) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      if (appState.loggedIn) ...[
        const Header('Discussion'),
        GuestBook(
          addMessage: (message) =>
              appState.addMessageToGuestBook(message),
          messages: appState.guestBookMessages, // new
        ),
      ],
    ],
  ),
),

Uji sinkronisasi pesan

Firestore secara otomatis dan instan menyinkronkan data dengan klien yang berlangganan database.

Uji sinkronisasi pesan:

  1. Di aplikasi, temukan pesan yang Anda buat sebelumnya di database.
  2. Tulis pesan baru. Mereka muncul seketika.
  3. Buka ruang kerja Anda di beberapa jendela atau tab. Pesan disinkronkan secara real time di seluruh jendela dan tab.
  4. Opsional: Di menu Database konsol Firebase, hapus, ubah, atau tambahkan pesan baru secara manual. Semua perubahan muncul di UI.

Selamat! Anda membaca dokumen Firestore di aplikasi Anda!

Pratinjau aplikasi

8. Siapkan aturan keamanan dasar

Anda awalnya menyiapkan Firestore untuk menggunakan mode pengujian, yang berarti database Anda terbuka untuk baca dan tulis. Namun, Anda sebaiknya hanya menggunakan mode pengujian selama tahap awal pengembangan. Sebagai praktik terbaik, Anda harus menyiapkan aturan keamanan untuk database saat mengembangkan aplikasi. Keamanan merupakan bagian tak terpisahkan dari struktur dan perilaku aplikasi Anda.

Aturan Keamanan Firebase memungkinkan Anda mengontrol akses ke dokumen dan koleksi di database Anda. Sintaks aturan yang fleksibel memungkinkan Anda membuat aturan yang cocok dengan apa pun mulai dari semua penulisan ke seluruh database hingga operasi pada dokumen tertentu.

Siapkan aturan keamanan dasar:

  1. Di menu Kembangkan Firebase console, klik Database > Rules . Anda akan melihat aturan keamanan default berikut dan peringatan tentang aturan yang bersifat publik:

7767a2d2e64e7275.png

  1. Identifikasi koleksi tempat aplikasi menulis data:

Di match /databases/{database}/documents , identifikasi koleksi yang ingin Anda amankan:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /guestbook/{entry} {
     // You'll add rules here in the next step.
  }
}

Karena Anda menggunakan UID Autentikasi sebagai bidang dalam setiap dokumen buku tamu, Anda bisa mendapatkan UID Autentikasi dan memverifikasi bahwa siapa pun yang mencoba menulis ke dokumen memiliki UID Autentikasi yang cocok.

  1. Tambahkan aturan baca dan tulis ke kumpulan aturan Anda:
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /guestbook/{entry} {
      allow read: if request.auth.uid != null;
      allow write:
        if request.auth.uid == request.resource.data.userId;
    }
  }
}

Sekarang, hanya pengguna yang masuk yang dapat membaca pesan di buku tamu, tetapi hanya pembuat pesan yang dapat mengedit pesan.

  1. Tambahkan validasi data untuk memastikan bahwa semua bidang yang diharapkan ada di dokumen:
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /guestbook/{entry} {
      allow read: if request.auth.uid != null;
      allow write:
      if request.auth.uid == request.resource.data.userId
          && "name" in request.resource.data
          && "text" in request.resource.data
          && "timestamp" in request.resource.data;
    }
  }
}

9. Langkah bonus: Latih apa yang telah Anda pelajari

Rekam status RSVP peserta

Saat ini, aplikasi Anda hanya memungkinkan orang untuk mengobrol saat mereka tertarik dengan acara tersebut. Selain itu, satu-satunya cara untuk mengetahui apakah seseorang akan datang adalah dengan mengatakannya dalam obrolan.

Pada langkah ini, Anda mengatur dan memberi tahu orang-orang berapa banyak orang yang akan datang. Anda menambahkan beberapa kemampuan ke status aplikasi. Yang pertama adalah kemampuan pengguna yang masuk untuk mencalonkan apakah mereka hadir. Yang kedua adalah penghitung berapa banyak orang yang hadir.

  1. Di file lib/main.dart , tambahkan baris berikut ke bagian pengakses agar kode UI dapat berinteraksi dengan status ini:

lib/main.dart

int _attendees = 0;
int get attendees => _attendees;

Attending _attending = Attending.unknown;
StreamSubscription<DocumentSnapshot>? _attendingSubscription;
Attending get attending => _attending;
set attending(Attending attending) {
  final userDoc = FirebaseFirestore.instance
      .collection('attendees')
      .doc(FirebaseAuth.instance.currentUser!.uid);
  if (attending == Attending.yes) {
    userDoc.set(<String, dynamic>{'attending': true});
  } else {
    userDoc.set(<String, dynamic>{'attending': false});
  }
}
  1. Perbarui metode init() ApplicationState sebagai berikut:

lib/main.dart

  Future<void> init() async {
    await Firebase.initializeApp(
        options: DefaultFirebaseOptions.currentPlatform);

    FirebaseUIAuth.configureProviders([
      EmailAuthProvider(),
    ]);

    // Add from here...
    FirebaseFirestore.instance
        .collection('attendees')
        .where('attending', isEqualTo: true)
        .snapshots()
        .listen((snapshot) {
      _attendees = snapshot.docs.length;
      notifyListeners();
    });
    // ...to here.

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
        _guestBookSubscription = FirebaseFirestore.instance
            .collection('guestbook')
            .orderBy('timestamp', descending: true)
            .snapshots()
            .listen((snapshot) {
          _guestBookMessages = [];
          for (final document in snapshot.docs) {
            _guestBookMessages.add(
              GuestBookMessage(
                name: document.data()['name'] as String,
                message: document.data()['text'] as String,
              ),
            );
          }
          notifyListeners();
        });
        // Add from here...
        _attendingSubscription = FirebaseFirestore.instance
            .collection('attendees')
            .doc(user.uid)
            .snapshots()
            .listen((snapshot) {
          if (snapshot.data() != null) {
            if (snapshot.data()!['attending'] as bool) {
              _attending = Attending.yes;
            } else {
              _attending = Attending.no;
            }
          } else {
            _attending = Attending.unknown;
          }
          notifyListeners();
        });
        // ...to here.
      } else {
        _loginState = ApplicationLoginState.loggedOut;
        _guestBookMessages = [];
        _guestBookSubscription?.cancel();
        _attendingSubscription?.cancel(); // new
      }
      notifyListeners();
    });
  }

Kode ini menambahkan kueri yang selalu berlangganan untuk menentukan jumlah peserta dan kueri kedua yang hanya aktif saat pengguna masuk untuk menentukan apakah pengguna hadir.

  1. Tambahkan enumerasi berikut setelah deklarasi GuestBookMessage :

lib/main.dart

enum Attending { yes, no, unknown }
  1. Tentukan widget baru yang berfungsi seperti tombol radio lama:

lib/main.dart

class YesNoSelection extends StatelessWidget {
  const YesNoSelection(
      {super.key, required this.state, required this.onSelection});
  final Attending state;
  final void Function(Attending selection) onSelection;

  @override
  Widget build(BuildContext context) {
    switch (state) {
      case Attending.yes:
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            children: [
              ElevatedButton(
                style: ElevatedButton.styleFrom(elevation: 0),
                onPressed: () => onSelection(Attending.yes),
                child: const Text('YES'),
              ),
              const SizedBox(width: 8),
              TextButton(
                onPressed: () => onSelection(Attending.no),
                child: const Text('NO'),
              ),
            ],
          ),
        );
      case Attending.no:
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            children: [
              TextButton(
                onPressed: () => onSelection(Attending.yes),
                child: const Text('YES'),
              ),
              const SizedBox(width: 8),
              ElevatedButton(
                style: ElevatedButton.styleFrom(elevation: 0),
                onPressed: () => onSelection(Attending.no),
                child: const Text('NO'),
              ),
            ],
          ),
        );
      default:
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            children: [
              StyledButton(
                onPressed: () => onSelection(Attending.yes),
                child: const Text('YES'),
              ),
              const SizedBox(width: 8),
              StyledButton(
                onPressed: () => onSelection(Attending.no),
                child: const Text('NO'),
              ),
            ],
          ),
        );
    }
  }
}

Itu dimulai dalam keadaan tak tentu dengan tidak memilih Ya atau Tidak . Setelah pengguna memilih apakah mereka akan hadir, Anda menampilkan opsi yang disorot dengan tombol terisi dan opsi lainnya menghilang dengan rendering datar.

  1. Perbarui metode build() HomePage untuk memanfaatkan YesNoSelection , aktifkan pengguna yang masuk untuk mencalonkan apakah mereka hadir, dan tampilkan jumlah peserta untuk acara tersebut:

lib/main.dart

Consumer<ApplicationState>(
  builder: (context, appState, _) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      // Add from here...
      if (appState.attendees >= 2)
        Paragraph('${appState.attendees} people going')
      else if (appState.attendees == 1)
        const Paragraph('1 person going')
      else
        const Paragraph('No one going'),
      // ...to here.
      if (appState.loggedIn) ...[
        // Add from here...
        YesNoSelection(
          state: appState.attending,
          onSelection: (attending) => appState.attending = attending,
        ),
        // ...to here.
        const Header('Discussion'),
        GuestBook(
          addMessage: (message) =>
              appState.addMessageToGuestBook(message),
          messages: appState.guestBookMessages,
        ),
      ],
    ],
  ),
),

Tambahkan aturan

Anda sudah menyiapkan beberapa aturan, sehingga data yang Anda tambahkan dengan tombol akan ditolak. Anda perlu memperbarui aturan untuk mengizinkan penambahan koleksi attendees .

  1. Di koleksi attendees , ambil UID Otentikasi yang Anda gunakan sebagai nama dokumen dan verifikasi bahwa uid pengirim sama dengan dokumen yang mereka tulis:
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // ... //
    match /attendees/{userId} {
      allow read: if true;
      allow write: if request.auth.uid == userId;
    }
  }
}

Ini memungkinkan semua orang membaca daftar peserta karena tidak ada data pribadi di sana, tetapi hanya pembuatnya yang dapat memperbaruinya.

  1. Tambahkan validasi data untuk memastikan bahwa semua bidang yang diharapkan ada di dokumen:
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // ... //
    match /attendees/{userId} {
      allow read: if true;
      allow write: if request.auth.uid == userId
          && "attending" in request.resource.data;

    }
  }
}
  1. Opsional: Di aplikasi, klik tombol untuk melihat hasilnya di dasbor Firestore di Firebase console.

Pratinjau aplikasi

10. Selamat!

Anda menggunakan Firebase untuk membangun aplikasi web real-time yang interaktif!

Belajarlah lagi