1. Sebelum Anda mulai
Dalam codelab ini, Anda akan mempelajari beberapa dasar Firebase untuk membuat aplikasi seluler Flutter untuk Android dan iOS.
Prasyarat
Codelab ini menganggap Anda sudah familiar dengan Flutter, dan Anda telah menginstal Flutter SDK , dan editor .
Apa yang akan Anda buat
Dalam codelab ini Anda akan membuat aplikasi obrolan RSVP dan buku tamu acara di Android, iOS, Web, dan macOS menggunakan Flutter. Anda akan mengautentikasi pengguna dengan Firebase Authentication dan menyinkronkan data menggunakan Cloud Firestore.
Apa yang Anda butuhkan?
Anda dapat menjalankan codelab ini menggunakan salah satu perangkat berikut:
- Perangkat fisik (Android atau iOS) yang terhubung ke komputer Anda dan disetel ke mode pengembang.
- Simulator iOS. (Memerlukan penginstalan alat Xcode .)
- Emulator Androidnya. (Memerlukan penyiapan di Android Studio .)
Selain hal di atas, Anda juga membutuhkan:
- Peramban pilihan Anda, seperti Chrome.
- IDE atau editor teks pilihan Anda, seperti Android Studio atau VS Code yang dikonfigurasi dengan plugin Dart dan Flutter.
- Flutter versi
stable
terbaru (ataubeta
jika Anda senang hidup di tepian). - Akun Google, seperti akun gmail, untuk membuat dan mengelola proyek Firebase Anda.
- Alat baris perintah
firebase
, masuk ke akun gmail Anda. - Kode sampel codelab. Lihat langkah selanjutnya untuk cara mendapatkan kode.
2. Dapatkan kode sampel
Mari kita mulai dengan mengunduh versi awal proyek kita dari GitHub.
Kloning repositori GitHub dari baris perintah:
git clone https://github.com/flutter/codelabs.git flutter-codelabs
Atau, jika Anda menginstal alat cli GitHub :
gh repo clone flutter/codelabs flutter-codelabs
Kode sampel harus dikloning ke direktori flutter-codelabs
, yang berisi kode untuk kumpulan codelabs. Kode untuk codelab ini ada di flutter-codelabs/firebase-get-to-know-flutter
.
Struktur direktori di bawah flutter-codelabs/firebase-get-to-know-flutter
adalah serangkaian snapshot di mana Anda seharusnya berada di akhir setiap langkah yang disebutkan. Ini adalah Langkah 2, jadi menemukan file yang cocok semudah:
cd flutter-codelabs/firebase-get-to-know-flutter/step_02
Jika Anda ingin melompat ke depan, atau melihat seperti apa tampilan setelah langkah, lihat di direktori yang dinamai sesuai langkah yang Anda minati.
Impor aplikasi pemula
Buka atau impor flutter-codelabs/firebase-get-to-know-flutter/step_02
ke IDE pilihan Anda. Direktori ini berisi kode awal untuk codelab yang terdiri dari aplikasi pertemuan Flutter yang belum berfungsi.
Temukan file untuk dikerjakan
Kode dalam aplikasi ini tersebar di beberapa direktori. Pemisahan fungsi ini dirancang untuk mempermudah pengerjaan, dengan mengelompokkan kode berdasarkan fungsionalitas.
Temukan file berikut dalam proyek:
-
lib/main.dart
: File ini berisi titik masuk utama dan widget aplikasi. -
lib/src/widgets.dart
: File ini berisi beberapa widget untuk membantu menstandardisasi gaya aplikasi. Ini digunakan untuk membuat layar aplikasi pemula. -
lib/src/authentication.dart
: File ini berisi implementasi sebagian dari FirebaseUI Auth dengan seperangkat widget untuk menciptakan pengalaman pengguna login untuk otentikasi berbasis email Firebase. Widget untuk alur autentikasi ini belum digunakan di aplikasi pemula, tetapi Anda akan segera menghubungkannya.
Anda akan menambahkan file tambahan yang diperlukan untuk membangun sisa aplikasi.
Meninjau file lib/main.dart
Aplikasi ini memanfaatkan paket google_fonts
untuk memungkinkan kami menjadikan Roboto sebagai font default di seluruh aplikasi. Latihan untuk pembaca yang termotivasi adalah menjelajahi fonts.google.com dan menggunakan font yang Anda temukan di sana di berbagai bagian aplikasi.
Anda menggunakan widget pembantu dari lib/src/widgets.dart
dalam bentuk Header
, Paragraph
dan IconAndDetail
. Widget ini mengurangi kekacauan dalam tata letak halaman yang dijelaskan di HomePage
dengan menghilangkan kode duplikat. Ini memiliki manfaat tambahan untuk memungkinkan tampilan dan nuansa yang konsisten.
Berikut tampilan aplikasi Anda di Android, iOS, Web, dan macOS:
Pratinjau aplikasi
3. Buat dan siapkan proyek Firebase
Menampilkan informasi acara sangat bagus untuk tamu Anda, tetapi hanya menampilkan acara tidak terlalu berguna bagi siapa pun. Mari tambahkan beberapa fungsi dinamis ke aplikasi ini. Untuk ini, Anda harus menghubungkan Firebase ke aplikasi Anda. Untuk memulai Firebase, Anda harus membuat dan menyiapkan proyek Firebase.
Buat proyek Firebase
- Masuk ke Firebase .
- Di Firebase console, klik Tambahkan Proyek (atau Buat proyek ), dan beri nama proyek Firebase Anda dengan Firebase-Flutter-Codelab .
- Klik melalui opsi pembuatan proyek. Terima persyaratan Firebase jika diminta. Lewati penyiapan Google Analytics, karena Anda tidak akan menggunakan Analytics untuk aplikasi ini.
Untuk mempelajari lebih lanjut tentang proyek Firebase, lihat Memahami proyek Firebase .
Aplikasi yang Anda buat menggunakan beberapa produk Firebase yang tersedia untuk aplikasi web:
- Firebase Authentication untuk mengizinkan pengguna Anda masuk ke aplikasi Anda.
- Cloud Firestore untuk menyimpan data terstruktur di cloud dan mendapatkan notifikasi instan saat data berubah.
- Aturan Keamanan Firebase untuk mengamankan database Anda.
Beberapa produk ini memerlukan konfigurasi khusus atau perlu diaktifkan menggunakan Firebase console.
Aktifkan login email untuk Firebase Authentication
Untuk mengizinkan pengguna masuk ke aplikasi web, Anda akan menggunakan metode masuk Email/Sandi untuk codelab ini:
- Di konsol Firebase, perluas menu Build di panel kiri.
- Klik Otentikasi , lalu klik tombol Mulai , lalu tab Metode masuk (atau klik di sini untuk langsung membuka tab Metode masuk ).
- Klik Email/Sandi di daftar Penyedia masuk, atur sakelar Aktifkan ke posisi aktif, lalu klik Simpan .
Aktifkan Cloud Firestore
Aplikasi web menggunakan Cloud Firestore untuk menyimpan pesan obrolan dan menerima pesan obrolan baru.
Aktifkan Cloud Firestore:
- Di bagian Build Firebase console, klik Cloud Firestore .
- Klik Buat basis data .
- Pilih opsi Mulai dalam mode uji . Baca penafian tentang aturan keamanan. Mode uji memastikan bahwa Anda dapat dengan bebas menulis ke database selama pengembangan. Klik Berikutnya .
- Pilih lokasi untuk database Anda (Anda bisa menggunakan default). Perhatikan bahwa lokasi ini tidak dapat diubah nanti.
- Klik Aktifkan .
4. Konfigurasi Firebase
Untuk menggunakan Firebase dengan Flutter, Anda harus mengikuti proses untuk mengonfigurasi proyek Flutter agar dapat menggunakan library FlutterFire dengan benar:
- Tambahkan dependensi FlutterFire ke proyek Anda
- Daftarkan platform yang diinginkan pada proyek Firebase
- Unduh file konfigurasi khusus platform, dan tambahkan ke kode.
Di direktori tingkat atas aplikasi Flutter Anda, ada subdirektori bernama android
, ios
, macos
dan web
. Direktori ini masing-masing menyimpan file konfigurasi khusus platform untuk iOS dan Android.
Konfigurasikan dependensi
Anda perlu menambahkan pustaka FlutterFire untuk dua produk Firebase yang Anda gunakan di aplikasi ini - Firebase Auth dan Cloud Firestore. Jalankan tiga perintah berikut untuk menambahkan dependensi.
$ flutter pub add firebase_core Resolving dependencies... + firebase_core 1.10.5 + firebase_core_platform_interface 4.2.2 + firebase_core_web 1.5.2 + flutter_web_plugins 0.0.0 from sdk flutter + js 0.6.3 test_api 0.4.3 (0.4.8 available) Changed 5 dependencies!
firebase_core
adalah kode umum yang diperlukan untuk semua plugin Firebase Flutter.
$ flutter pub add firebase_auth Resolving dependencies... + firebase_auth 3.3.3 + firebase_auth_platform_interface 6.1.8 + firebase_auth_web 3.3.4 + intl 0.17.0 test_api 0.4.3 (0.4.8 available) Changed 4 dependencies!
firebase_auth
memungkinkan integrasi dengan kemampuan Otentikasi Firebase.
$ flutter pub add cloud_firestore Resolving dependencies... + cloud_firestore 3.1.4 + cloud_firestore_platform_interface 5.4.9 + cloud_firestore_web 2.6.4 test_api 0.4.3 (0.4.8 available) Changed 3 dependencies!
cloud_firestore
memungkinkan akses ke penyimpanan data Cloud Firestore.
$ flutter pub add provider Resolving dependencies... + nested 1.0.0 + provider 6.0.1 test_api 0.4.3 (0.4.8 available) Changed 2 dependencies!
Meskipun Anda telah menambahkan paket yang diperlukan, Anda juga perlu mengonfigurasi proyek iOS, Android, macOS, dan Web runner untuk menggunakan Firebase dengan tepat. Anda juga menggunakan paket provider
yang akan memungkinkan pemisahan logika bisnis dari logika tampilan.
Memasang flutterfire
FlutterFire CLI bergantung pada Firebase CLI yang mendasarinya. Jika Anda belum melakukannya, pastikan Firebase CLI diinstal pada mesin Anda.
Selanjutnya, instal FlutterFire CLI dengan menjalankan perintah berikut:
$ dart pub global activate flutterfire_cli
Setelah diinstal, perintah flutterfire
akan tersedia secara global.
Mengonfigurasi 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 akan memandu Anda melalui sejumlah proses:
- Memilih proyek Firebase (berdasarkan file .firebaserc atau dari Firebase Console).
- Konfirmasi platform apa (misalnya Android, iOS, macOS & web) yang Anda inginkan untuk konfigurasi.
- Identifikasi aplikasi Firebase untuk platform yang dipilih yang harus digunakan untuk mengekstrak konfigurasi. Secara default, CLI akan mencoba mencocokkan aplikasi Firebase secara otomatis berdasarkan konfigurasi proyek Anda saat ini.
- Buat file firebase_options.dart di proyek Anda.
Konfigurasikan macOS
Flutter di macOS membangun aplikasi yang sepenuhnya terkotak pasir. Karena aplikasi ini berintegrasi menggunakan 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>
Lihat Kepemilikan dan Kotak Pasir Aplikasi untuk detail selengkapnya.
5. Tambahkan login pengguna (RSVP)
Sekarang setelah Anda menambahkan Firebase ke aplikasi, Anda dapat menyiapkan tombol RSVP yang mendaftarkan orang menggunakan Firebase Authentication . Untuk bawaan Android, bawaan iOS, dan Web ada paket FirebaseUI Auth bawaan, tetapi untuk Flutter Anda perlu membangun kemampuan ini.
Proyek yang Anda ambil di Langkah 2 menyertakan satu set widget yang mengimplementasikan antarmuka pengguna untuk sebagian besar alur autentikasi. Anda akan menerapkan logika bisnis untuk mengintegrasikan Firebase Authentication ke dalam aplikasi.
Logika Bisnis dengan Penyedia
Anda akan menggunakan paket provider
untuk membuat objek status aplikasi terpusat tersedia di seluruh pohon aplikasi widget Flutter. Untuk memulainya, ubah impor di bagian atas lib/main.dart
:
lib/main.dart
import 'package:firebase_auth/firebase_auth.dart'; // new
import 'package:firebase_core/firebase_core.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';
Baris import
memperkenalkan Firebase Core dan Auth, menarik paket provider
yang Anda gunakan untuk membuat objek status aplikasi tersedia melalui pohon widget, dan menyertakan widget otentikasi dari lib/src
.
Objek status aplikasi ini, ApplicationState
, memiliki dua tanggung jawab utama untuk langkah ini, tetapi akan mendapatkan tanggung jawab tambahan saat Anda menambahkan lebih banyak kemampuan ke aplikasi di langkah selanjutnya. Tanggung jawab pertama adalah menginisialisasi pustaka Firebase dengan panggilan ke Firebase.initializeApp()
, dan kemudian ada penanganan alur otorisasi. Tambahkan kelas berikut ke akhir lib/main.dart
:
lib/main.dart
class ApplicationState extends ChangeNotifier {
ApplicationState() {
init();
}
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loginState = ApplicationLoginState.loggedIn;
} else {
_loginState = ApplicationLoginState.loggedOut;
}
notifyListeners();
});
}
ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
ApplicationLoginState get loginState => _loginState;
String? _email;
String? get email => _email;
void startLoginFlow() {
_loginState = ApplicationLoginState.emailAddress;
notifyListeners();
}
Future<void> verifyEmail(
String email,
void Function(FirebaseAuthException e) errorCallback,
) async {
try {
var methods =
await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);
if (methods.contains('password')) {
_loginState = ApplicationLoginState.password;
} else {
_loginState = ApplicationLoginState.register;
}
_email = email;
notifyListeners();
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
Future<void> signInWithEmailAndPassword(
String email,
String password,
void Function(FirebaseAuthException e) errorCallback,
) async {
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void cancelRegistration() {
_loginState = ApplicationLoginState.emailAddress;
notifyListeners();
}
Future<void> registerAccount(
String email,
String displayName,
String password,
void Function(FirebaseAuthException e) errorCallback) async {
try {
var credential = await FirebaseAuth.instance
.createUserWithEmailAndPassword(email: email, password: password);
await credential.user!.updateDisplayName(displayName);
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
void signOut() {
FirebaseAuth.instance.signOut();
}
}
Perlu dicatat beberapa poin penting dalam kelas ini. Pengguna memulai tanpa otentikasi, aplikasi menunjukkan formulir yang meminta alamat email pengguna, tergantung pada apakah alamat email itu ada di file, aplikasi akan meminta pengguna mendaftar, atau meminta kata sandi mereka, dan kemudian mengasumsikan semuanya berhasil, pengguna diautentikasi.
Harus diperhatikan bahwa ini bukan implementasi lengkap dari alur FirebaseUI Auth, karena tidak menangani kasus pengguna dengan akun yang ada yang mengalami masalah saat masuk. Menerapkan kemampuan tambahan ini dibiarkan sebagai latihan untuk pembaca yang termotivasi.
Mengintegrasikan alur Otentikasi
Sekarang setelah Anda memulai status aplikasi, sekarang saatnya untuk menghubungkan status aplikasi ke inisialisasi aplikasi dan menambahkan aliran otentikasi ke HomePage
. Perbarui titik masuk utama untuk mengintegrasikan status aplikasi melalui paket provider
:
lib/main.dart
void main() {
// Modify from here
runApp(
ChangeNotifierProvider(
create: (context) => ApplicationState(),
builder: (context, _) => App(),
),
);
// to here.
}
Modifikasi fungsi main
membuat paket penyedia bertanggung jawab untuk membuat instance objek status aplikasi menggunakan widget ChangeNotifierProvider
. Anda menggunakan kelas penyedia khusus ini karena objek status aplikasi memperluas ChangeNotifier
dan ini memungkinkan paket provider
mengetahui kapan harus menampilkan ulang widget yang bergantung. Terakhir, integrasikan status aplikasi dengan Authentication
dengan memperbarui metode build
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'),
// Add from here
Consumer<ApplicationState>(
builder: (context, appState, _) => Authentication(
email: appState.email,
loginState: appState.loginState,
startLoginFlow: appState.startLoginFlow,
verifyEmail: appState.verifyEmail,
signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
cancelRegistration: appState.cancelRegistration,
registerAccount: appState.registerAccount,
signOut: appState.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 Authentication
, dan membungkusnya dalam widget Consumer
. Widget Konsumen cara yang biasa digunakan paket provider
untuk membangun kembali bagian dari pohon ketika status aplikasi berubah. Widget Authentication
adalah UI otentikasi yang sekarang akan Anda uji.
Menguji alur Otentikasi
Berikut adalah awal dari alur otentikasi, di mana pengguna dapat menekan tombol RSVP, untuk memulai formulir email.
Setelah memasukkan email, sistem mengkonfirmasi jika pengguna sudah terdaftar, dalam hal ini pengguna dimintai kata sandi, atau jika pengguna tidak terdaftar, maka mereka pergi melalui formulir pendaftaran.
Pastikan untuk mencoba memasukkan kata sandi pendek (kurang dari enam karakter) untuk memeriksa alur penanganan kesalahan. Jika pengguna terdaftar, mereka akan melihat kata sandi sebagai gantinya.
Pada halaman ini pastikan untuk memasukkan password yang salah untuk memeriksa penanganan error pada halaman ini. Terakhir, setelah pengguna masuk, Anda akan melihat pengalaman masuk yang menawarkan pengguna kemampuan untuk keluar lagi.
Dan dengan itu, Anda telah menerapkan alur otentikasi. Selamat!
6. Tulis pesan ke Cloud Firestore
Mengetahui bahwa pengguna akan datang adalah hal yang bagus, tetapi mari berikan 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 akan menggunakan Cloud Firestore .
Model data
Cloud Firestore adalah database NoSQL, dan data yang disimpan dalam database dibagi menjadi koleksi, dokumen, kolom, dan subkoleksi. Anda akan menyimpan setiap pesan obrolan sebagai dokumen dalam koleksi tingkat atas yang disebut guestbook
.
Tambahkan pesan ke Firestore
Di bagian ini, Anda akan menambahkan fungsionalitas bagi pengguna untuk menulis pesan baru ke database. Pertama, Anda menambahkan elemen UI (bidang formulir dan tombol kirim), lalu Anda menambahkan kode yang mengaitkan elemen-elemen ini ke database.
Pertama, 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';
Untuk membuat elemen UI bidang pesan dan tombol kirim, tambahkan widget stateful GuestBook
baru di bagian bawah lib/main.dart
.
lib/main.dart
class GuestBook extends StatefulWidget {
const GuestBook({required this.addMessage});
final FutureOr<void> Function(String message) addMessage;
@override
_GuestBookState 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 pesan yang sebenarnya memiliki beberapa konten, dan menunjukkan kepada pengguna pesan kesalahan jika tidak ada. Cara untuk memvalidasi formulir melibatkan mengakses status formulir di belakang formulir, dan untuk ini Anda menggunakan GlobalKey
. Untuk informasi lebih lanjut tentang Tombol, dan cara menggunakannya, silakan lihat Flutter Widgets 101 episode "Kapan Menggunakan Tombol" .
Perhatikan juga cara widget ditata, Anda memiliki Row
, dengan TextFormField
dan StyledButton
, yang berisi sendiri Row
. Perhatikan juga TextFormField
dibungkus dalam widget yang Expanded
, ini memaksa TextFormField
untuk mengambil ruang ekstra di baris. Untuk lebih memahami mengapa ini diperlukan, silakan baca Memahami kendala .
Sekarang setelah Anda memiliki widget yang memungkinkan pengguna memasukkan beberapa teks untuk ditambahkan ke Buku Tamu, Anda perlu menampilkannya di layar. Untuk melakukannya, edit isi HomePage
untuk menambahkan dua baris berikut di bagian bawah 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 ini cukup untuk menampilkan Widget, itu tidak cukup untuk melakukan sesuatu yang berguna. Anda akan segera memperbarui kode ini agar berfungsi.
Pratinjau aplikasi
Pengguna yang mengeklik tombol KIRIM akan memicu cuplikan kode di bawah ini. Itu menambahkan isi bidang input pesan ke koleksi guestbook
dari database. Secara khusus, metode addMessageToGuestBook
menambahkan konten pesan ke dokumen baru (dengan ID yang dibuat secara otomatis) ke koleksi guestbook
.
Perhatikan bahwa FirebaseAuth.instance.currentUser.uid
adalah referensi ke ID unik yang dibuat secara otomatis yang diberikan Firebase Authentication untuk semua pengguna yang masuk.
Buat perubahan lain pada file lib/main.dart
. Tambahkan metode addMessageToGuestBook
. Anda akan menghubungkan antarmuka pengguna dan kemampuan ini bersama-sama di langkah berikutnya.
lib/main.dart
class ApplicationState extends ChangeNotifier {
// Current content of ApplicationState elided ...
// Add from here
Future<DocumentReference> addMessageToGuestBook(String message) {
if (_loginState != ApplicationLoginState.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
}
Menghubungkan UI ke database
Anda memiliki UI tempat pengguna dapat memasukkan teks yang ingin mereka tambahkan ke Buku Tamu, dan Anda memiliki kode untuk menambahkan entri ke Cloud Firestore. Sekarang yang perlu Anda lakukan adalah menyambungkan keduanya. Di lib/main.dart
buat 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, _) => Authentication(
email: appState.email,
loginState: appState.loginState,
startLoginFlow: appState.startLoginFlow,
verifyEmail: appState.verifyEmail,
signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
cancelRegistration: appState.cancelRegistration,
registerAccount: appState.registerAccount,
signOut: appState.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.loginState == ApplicationLoginState.loggedIn) ...[
const Header('Discussion'),
GuestBook(
addMessage: (message) =>
appState.addMessageToGuestBook(message),
),
],
],
),
),
// To here.
],
),
);
}
}
Anda telah mengganti dua baris yang Anda tambahkan kembali 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 untuk bereaksi terhadap seseorang yang memasukkan pesan di UI, dan memublikasikannya ke dalam database. Di bagian berikutnya Anda akan menguji apakah pesan yang ditambahkan dipublikasikan ke dalam database.
Uji pengiriman pesan
- Pastikan Anda masuk ke aplikasi.
- Masukkan pesan seperti "Hai!", lalu klik KIRIM .
Tindakan ini menulis pesan ke database Cloud Firestore Anda. Namun, Anda belum akan melihat pesan tersebut di aplikasi Flutter Anda yang sebenarnya karena Anda masih perlu menerapkan pengambilan data. Anda akan melakukannya di langkah berikutnya.
Tetapi Anda dapat melihat pesan yang baru ditambahkan di konsol Firebase.
Di konsol Firebase, di dasbor Database , Anda akan melihat koleksi guestbook
dengan pesan yang baru ditambahkan. Jika Anda terus mengirim pesan, koleksi buku tamu Anda akan berisi banyak dokumen, seperti ini:
Konsol Firebase
7. Baca pesan
Sangat menyenangkan bahwa para tamu dapat menulis pesan ke database, tetapi mereka belum dapat melihatnya di aplikasi. Mari kita perbaiki itu!
Sinkronkan pesan
Untuk menampilkan pesan, Anda harus menambahkan listener yang memicu saat data berubah, lalu membuat elemen UI yang menampilkan pesan baru. Anda akan menambahkan kode ke status aplikasi yang mendengarkan pesan yang baru ditambahkan dari aplikasi.
Tepat di atas widget GuestBook
, kelas nilai berikut. Kelas ini menampilkan tampilan terstruktur dari data yang Anda simpan di Cloud Firestore.
lib/main.dart
class GuestBookMessage {
GuestBookMessage({required this.name, required this.message});
final String name;
final String message;
}
Di bagian ApplicationState
tempat Anda menentukan status dan getter, tambahkan baris baru berikut:
lib/main.dart
ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
ApplicationLoginState get loginState => _loginState;
String? _email;
String? get email => _email;
// Add from here
StreamSubscription<QuerySnapshot>? _guestBookSubscription;
List<GuestBookMessage> _guestBookMessages = [];
List<GuestBookMessage> get guestBookMessages => _guestBookMessages;
// to here.
Dan akhirnya, di bagian inisialisasi ApplicationState
, tambahkan yang berikut ini untuk berlangganan kueri atas kumpulan dokumen saat pengguna masuk, dan berhenti berlangganan saat mereka keluar.
lib/main.dart
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loginState = ApplicationLoginState.loggedIn;
// Add from here
_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();
});
// to here.
} else {
_loginState = ApplicationLoginState.loggedOut;
// Add from here
_guestBookMessages = [];
_guestBookSubscription?.cancel();
// to here.
}
notifyListeners();
});
}
Bagian ini penting, karena di sinilah Anda membuat kueri atas koleksi guestbook
, dan menangani berlangganan dan berhenti berlangganan koleksi ini. Anda mendengarkan aliran, di mana Anda merekonstruksi cache lokal dari pesan dalam koleksi guestbook
, dan juga menyimpan referensi ke langganan ini sehingga Anda dapat berhenti berlangganan nanti. Ada banyak hal yang terjadi di sini, dan ada baiknya menghabiskan waktu di debugger untuk memeriksa apa yang terjadi ketika mendapatkan model mental yang lebih jelas.
Untuk informasi selengkapnya, lihat dokumentasi Cloud Firestore .
Di widget GuestBook
Anda perlu menghubungkan status perubahan ini ke antarmuka pengguna. Anda memodifikasi widget dengan menambahkan daftar pesan sebagai bagian dari konfigurasinya.
lib/main.dart
class GuestBook extends StatefulWidget {
// Modify the following line
const GuestBook({required this.addMessage, required this.messages});
final FutureOr<void> Function(String message) addMessage;
final List<GuestBookMessage> messages; // new
@override
_GuestBookState createState() => _GuestBookState();
}
Selanjutnya, kami mengekspos konfigurasi baru ini di _GuestBookState
dengan memodifikasi metode build
sebagai berikut.
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
, lalu di ekor anak-anak Column
Anda menambahkan koleksi untuk menghasilkan Paragraph
baru untuk setiap pesan dalam daftar pesan.
Terakhir, Anda sekarang perlu memperbarui isi HomePage
untuk menyusun GuestBook
dengan benar dengan parameter messages
baru.
lib/main.dart
Consumer<ApplicationState>(
builder: (context, appState, _) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (appState.loginState == ApplicationLoginState.loggedIn) ...[
const Header('Discussion'),
GuestBook(
addMessage: (message) =>
appState.addMessageToGuestBook(message),
messages: appState.guestBookMessages, // new
),
],
],
),
),
Uji sinkronisasi pesan
Cloud Firestore secara otomatis dan instan menyinkronkan data dengan klien yang berlangganan database.
- Pesan yang Anda buat sebelumnya di database harus ditampilkan di aplikasi. Jangan ragu untuk menulis pesan baru; mereka akan muncul secara instan.
- Jika Anda membuka ruang kerja di beberapa jendela atau tab, pesan akan disinkronkan secara waktu nyata di seluruh tab.
- (Opsional) Anda dapat mencoba menghapus, memodifikasi, atau menambahkan pesan baru secara manual langsung di bagian Database di Firebase console; perubahan apa pun akan muncul di UI.
Selamat! Anda sedang membaca dokumen Cloud Firestore di aplikasi Anda!
Ulasan aplikasi
8. Siapkan aturan keamanan dasar
Anda awalnya menyiapkan Cloud Firestore untuk menggunakan mode pengujian, artinya database Anda terbuka untuk membaca dan menulis. Namun, Anda hanya boleh menggunakan mode uji selama tahap pengembangan yang sangat awal. Sebagai praktik terbaik, Anda harus menyiapkan aturan keamanan untuk database saat mengembangkan aplikasi. Keamanan harus menjadi bagian integral dari struktur dan perilaku aplikasi Anda.
Aturan Keamanan 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.
Anda dapat menulis aturan keamanan untuk Cloud Firestore di Firebase console:
- Di bagian Kembangkan konsol Firebase, klik Database , lalu pilih tab Aturan (atau klik di sini untuk langsung membuka tab Aturan ).
- Anda akan melihat aturan keamanan default berikut, bersama dengan peringatan tentang aturan yang bersifat publik.
Identifikasi koleksi
Pertama, identifikasi koleksi tempat aplikasi menulis data.
Dalam 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.
}
}
Tambahkan aturan keamanan
Karena Anda menggunakan UID Otentikasi sebagai bidang di setiap dokumen buku tamu, Anda bisa mendapatkan UID Otentikasi dan memverifikasi bahwa siapa pun yang mencoba menulis ke dokumen memiliki UID Otentikasi yang cocok.
Tambahkan aturan baca dan tulis ke set aturan Anda seperti yang ditunjukkan di bawah ini:
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, untuk buku tamu, hanya pengguna yang masuk yang dapat membaca pesan (pesan apa pun!), tetapi hanya pembuat pesan yang dapat mengedit pesan.
Tambahkan aturan validasi
Tambahkan validasi data untuk memastikan bahwa semua bidang yang diharapkan ada dalam 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
Catat status RSVP peserta
Saat ini, aplikasi Anda hanya memungkinkan orang untuk mulai mengobrol jika mereka tertarik dengan acara tersebut. Juga, satu-satunya cara Anda tahu jika seseorang datang adalah jika mereka mempostingnya di obrolan. Mari kita mengatur dan memberi tahu orang-orang berapa banyak orang yang datang.
Anda akan menambahkan beberapa kemampuan baru ke status aplikasi. Yang pertama adalah kemampuan bagi pengguna yang masuk untuk mencalonkan apakah mereka hadir atau tidak. Kemampuan kedua adalah penghitung berapa banyak orang yang benar-benar hadir.
Di lib/main.dart
, tambahkan berikut ini ke bagian pengakses untuk mengaktifkan kode UI untuk 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});
}
}
Perbarui metode init
ApplicationState
sebagai berikut:
lib/main.dart
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
// 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();
});
}
Di atas menambahkan kueri selalu berlangganan untuk mengetahui jumlah peserta, dan kueri kedua yang hanya aktif saat pengguna masuk untuk mengetahui apakah pengguna hadir. Selanjutnya, tambahkan enumerasi berikut setelah deklarasi GuestBookMessage
:
lib/main.dart
enum Attending { yes, no, unknown }
Anda sekarang akan menentukan widget baru yang berfungsi seperti tombol radio lama. Ini dimulai dalam keadaan tak tentu, dengan tidak memilih ya atau tidak, tetapi setelah pengguna memilih apakah mereka hadir atau tidak, maka Anda menunjukkan opsi yang disorot dengan tombol yang diisi, dan opsi lainnya surut dengan rendering datar.
lib/main.dart
class YesNoSelection extends StatelessWidget {
const YesNoSelection({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'),
),
],
),
);
}
}
}
Selanjutnya, Anda perlu memperbarui metode pembuatan HomePage
untuk memanfaatkan YesNoSelection
, memungkinkan pengguna yang masuk untuk mencalonkan jika mereka hadir. Anda juga akan menampilkan jumlah peserta untuk acara ini.
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.loginState == ApplicationLoginState.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
Karena Anda sudah menyiapkan beberapa aturan, data baru yang Anda tambahkan dengan tombol akan ditolak. Anda harus memperbarui aturan untuk mengizinkan penambahan ke koleksi attendees
.
Untuk koleksi attendees
, karena Anda menggunakan UID Otentikasi sebagai nama dokumen, Anda dapat mengambilnya dan memverifikasi bahwa uid
pengirim sama dengan dokumen yang mereka tulis. Anda akan mengizinkan semua orang membaca daftar peserta (karena tidak ada data pribadi di sana), tetapi hanya pembuatnya yang dapat memperbaruinya.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ... //
match /attendees/{userId} {
allow read: if true;
allow write: if request.auth.uid == userId;
}
}
}
Tambahkan aturan validasi
Tambahkan validasi data untuk memastikan bahwa semua bidang yang diharapkan ada dalam 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;
}
}
}
(Opsional) Anda sekarang dapat melihat hasil mengklik tombol. Buka dasbor Cloud Firestore Anda di Firebase console.
Pratinjau aplikasi
10. Selamat!
Anda telah menggunakan Firebase untuk membuat aplikasi web waktu nyata yang interaktif!
Apa yang telah kita bahas
- Otentikasi Firebase
- Cloud Firestore
- Aturan Keamanan Firebase
Langkah selanjutnya
- Ingin mempelajari lebih lanjut tentang produk Firebase lainnya? Mungkin Anda ingin menyimpan file gambar yang diunggah pengguna? Atau mengirim pemberitahuan ke pengguna Anda? Lihat dokumentasi Firebase . Ingin mempelajari lebih lanjut tentang plugin Flutter untuk Firebase? Lihat FlutterFire untuk informasi lebih lanjut.
- Ingin mempelajari lebih lanjut tentang Cloud Firestore? Mungkin Anda ingin belajar tentang subkoleksi dan transaksi? Buka codelab web Cloud Firestore untuk codelab yang membahas lebih dalam tentang Cloud Firestore. Atau lihat serial YouTube ini untuk mengenal Cloud Firestore !
Belajarlah lagi
- Situs Firebase: firebase.google.com
- Situs Flutter: flutter.dev
- Widget FlutterFire Firebase Flutter: firebase.flutter.dev
- Saluran YouTube Firebase
- Saluran YouTube Flutter
Bagaimana hasilnya?
Kami akan menyukai tanggapan Anda! Silakan isi formulir (sangat) singkat di sini .