1. Sebelum Anda mulai
Dalam codelab ini, Anda mempelajari beberapa dasar-dasar Firebase untuk membuat aplikasi seluler Flutter untuk Android dan iOS.
Prasyarat
- Keakraban dengan Flutter
- SDK Flutter
- Editor teks pilihan Anda
Apa yang akan Anda pelajari
- Cara membuat aplikasi RSVP acara dan 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 perlukan
Salah satu perangkat berikut:
- Perangkat fisik Android atau iOS yang terhubung ke komputer Anda dan disetel ke mode pengembang.
- Simulator iOS (Membutuhkan alat Xcode ).
- Emulator Android (Memerlukan penyiapan di Android Studio ).
Anda juga memerlukan 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 .
- Versi
stable
terbaru dari Flutter ataubeta
jika Anda menikmati hidup di edge. - Akun Google untuk pembuatan dan pengelolaan proyek Firebase Anda.
-
Firebase
CLI login ke Akun Google Anda.
2. Dapatkan kode contoh
Unduh versi awal proyek Anda dari GitHub:
- Dari baris perintah, clone repositori GitHub di direktori
flutter-codelabs
:
git clone https://github.com/flutter/codelabs.git flutter-codelabs
Direktori flutter-codelabs
berisi kode untuk kumpulan codelab. Kode untuk codelab ini ada di direktori flutter-codelabs/firebase-get-to-know-flutter
. Direktori berisi serangkaian snapshot yang menunjukkan tampilan proyek Anda di akhir setiap langkah. Misalnya, Anda berada di langkah kedua.
- 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 tampilan sesuatu setelah suatu langkah, lihat di direktori yang diberi nama sesuai 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 diperbaiki
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/home_page.dart
: File ini berisi widget halaman beranda. -
lib/src/widgets.dart
: File ini berisi beberapa widget untuk membantu menstandarkan gaya aplikasi. Mereka menyusun layar aplikasi starter. -
lib/src/authentication.dart
: File ini berisi sebagian implementasi Otentikasi dengan serangkaian widget untuk menciptakan pengalaman pengguna login untuk otentikasi berbasis email Firebase. Widget untuk alur autentikasi ini belum digunakan di aplikasi awal, namun Anda akan segera menambahkannya.
-
Anda menambahkan file tambahan sesuai kebutuhan untuk membangun aplikasi lainnya.
Tinjau file lib/main.dart
Aplikasi ini memanfaatkan paket google_fonts
untuk menjadikan Roboto sebagai font default di seluruh aplikasi. Anda dapat menjelajahi font.google.com dan menggunakan font yang Anda temukan di berbagai bagian aplikasi.
Anda menggunakan widget pembantu dari file lib/src/widgets.dart
berupa Header
, Paragraph
dan IconAndDetail
. Widget ini menghilangkan kode duplikat untuk mengurangi kekacauan pada tata letak halaman yang dijelaskan di HomePage
. Hal ini juga memungkinkan tampilan dan nuansa yang konsisten.
Berikut tampilan aplikasi Anda di Android, iOS, Web, dan macOS:
3. Membuat dan mengonfigurasi proyek Firebase
Tampilan informasi acara sangat bagus untuk tamu Anda, namun tidak terlalu berguna bagi 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
- Masuk ke Firebase .
- Di konsol, klik Tambahkan Proyek atau Buat proyek .
- Di bidang Nama proyek , masukkan Firebase-Flutter-Codelab lalu klik Lanjutkan .
- Klik opsi pembuatan proyek. Jika diminta, terima persyaratan Firebase, namun lewati penyiapan Google Analytics karena Anda tidak akan menggunakannya untuk aplikasi ini.
Untuk mempelajari lebih lanjut proyek Firebase, lihat Memahami proyek Firebase .
Aplikasi ini menggunakan produk Firebase berikut, yang tersedia untuk aplikasi web:
- Otentikasi: Memungkinkan pengguna masuk ke aplikasi Anda.
- Firestore: Menyimpan data terstruktur di cloud dan mendapat notifikasi instan saat data berubah.
- Aturan Keamanan Firebase: Mengamankan database Anda.
Beberapa produk ini memerlukan konfigurasi khusus atau Anda perlu mengaktifkannya di Firebase console.
Aktifkan autentikasi masuk email
- Di panel Ikhtisar Proyek di Firebase console, perluas menu Build .
- Klik Otentikasi > Memulai > Metode masuk > Email/Kata Sandi > Aktifkan > Simpan .
Aktifkan Firestore
Aplikasi web menggunakan Firestore untuk menyimpan pesan obrolan dan menerima pesan obrolan baru.
Aktifkan Firestore:
- Di menu Build , klik Firestore Database > Buat database .
- Pilih Mulai dalam mode pengujian , lalu baca penafian tentang aturan keamanan. Mode pengujian memastikan Anda dapat dengan bebas menulis ke database selama pengembangan.
- Klik Berikutnya dan kemudian pilih lokasi untuk database Anda. Anda dapat menggunakan default. Anda tidak dapat mengubah lokasinya nanti.
- Klik Aktifkan .
4. Konfigurasikan Firebase
Untuk menggunakan Firebase dengan Flutter, Anda perlu menyelesaikan tugas berikut untuk mengonfigurasi proyek Flutter agar menggunakan pustaka FlutterFire
dengan benar:
- Tambahkan dependensi
FlutterFire
ke proyek Anda. - Daftarkan platform yang diinginkan pada proyek Firebase.
- Unduh file konfigurasi khusus platform lalu tambahkan ke kode.
Di direktori tingkat 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 pustaka 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 Firebase Flutter.
$ flutter pub add firebase_auth
Paket firebase_auth
memungkinkan integrasi dengan Authentication.
$ flutter pub add cloud_firestore
Paket cloud_firestore
memungkinkan akses ke penyimpanan data Firestore.
$ flutter pub add provider
Paket firebase_ui_auth
menyediakan serangkaian widget dan utilitas untuk meningkatkan kecepatan developer dengan alur autentikasi.
$ flutter pub add firebase_ui_auth
Anda telah menambahkan paket yang diperlukan, namun Anda juga perlu mengonfigurasi proyek iOS, Android, macOS, dan Web runner agar dapat 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.
- Jika Anda belum melakukannya, instal Firebase CLI di mesin Anda.
- 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:
- Pilih proyek Firebase berdasarkan file
.firebaserc
atau dari Firebase Console. - Tentukan platform untuk konfigurasi, seperti Android, iOS, macOS, dan web.
- Identifikasi aplikasi Firebase yang akan digunakan untuk mengekstrak konfigurasi. Secara default, CLI 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 membuat aplikasi yang sepenuhnya di-sandbox. Saat 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-orang dengan Authentication . Untuk Android asli, iOS asli, dan Web, terdapat paket FirebaseUI Auth
bawaan, namun 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 Autentikasi dengan aplikasi.
Tambahkan logika bisnis dengan paket Provider
Gunakan paket provider
untuk membuat objek status aplikasi terpusat tersedia di seluruh pohon widget Flutter aplikasi:
- Buat file baru bernama
app_state.dart
dengan konten berikut:
lib/app_state.dart
import 'package:firebase_auth/firebase_auth.dart'
hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';
import 'firebase_options.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();
});
}
}
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 memperingatkan pohon widget bahwa ada pembaruan ke status terotentikasi.
Anda hanya menggunakan penyedia untuk mengomunikasikan status status login pengguna ke aplikasi. Untuk mengizinkan pengguna login, Anda menggunakan UI yang disediakan oleh paket firebase_ui_auth
, yang merupakan cara terbaik untuk mem-bootstrap layar login di aplikasi Anda dengan cepat.
Integrasikan alur autentikasi
- Ubah impor di bagian atas file
lib/main.dart
:
lib/main.dart
import 'package:firebase_ui_auth/firebase_ui_auth.dart'; // new
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; // new
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart'; // new
import 'app_state.dart'; // new
import 'home_page.dart';
- 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 pada fungsi main()
membuat paket penyedia bertanggung jawab atas pembuatan 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 ulang widget yang bergantung.
- Perbarui aplikasi Anda untuk menangani navigasi ke berbagai layar yang disediakan FirebaseUI untuk Anda, dengan membuat konfigurasi
GoRouter
:
lib/main.dart
// Add GoRouter configuration outside the App class
final _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomePage(),
routes: [
GoRoute(
path: 'sign-in',
builder: (context, state) {
return SignInScreen(
actions: [
ForgotPasswordAction(((context, email) {
final uri = Uri(
path: '/sign-in/forgot-password',
queryParameters: <String, String?>{
'email': email,
},
);
context.push(uri.toString());
})),
AuthStateChangeAction(((context, state) {
final user = switch (state) {
SignedIn state => state.user,
UserCreated state => state.credential.user,
_ => null
};
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);
}
context.pushReplacement('/');
})),
],
);
},
routes: [
GoRoute(
path: 'forgot-password',
builder: (context, state) {
final arguments = state.queryParameters;
return ForgotPasswordScreen(
email: arguments['email'],
headerMaxExtent: 200,
);
},
),
],
),
GoRoute(
path: 'profile',
builder: (context, state) {
return ProfileScreen(
providers: const [],
actions: [
SignedOutAction((context) {
context.pushReplacement('/');
}),
],
);
},
),
],
),
],
);
// end of GoRouter configuration
// Change MaterialApp to MaterialApp.router and add the routerConfig
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
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,
useMaterial3: true,
),
routerConfig: _router, // new
);
}
}
Setiap layar memiliki jenis tindakan berbeda yang terkait dengannya berdasarkan status baru alur autentikasi. Setelah sebagian besar perubahan status dalam autentikasi, Anda dapat merutekan ulang kembali ke layar pilihan, baik itu layar beranda atau layar lain, misalnya profil.
- Dalam metode build kelas
HomePage
, integrasikan status aplikasi dengan widgetAuthFunc
:
lib/home_page.dart
import 'package:firebase_auth/firebase_auth.dart' // new
hide EmailAuthProvider, PhoneAuthProvider; // new
import 'package:flutter/material.dart'; // new
import 'package:provider/provider.dart'; // new
import 'app_state.dart'; // new
import 'src/authentication.dart'; // new
import 'src/widgets.dart';
class HomePage extends StatelessWidget {
const HomePage({super.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, _) => 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 paket provider
untuk membangun kembali bagian pohon ketika status aplikasi berubah. Widget AuthFunc
adalah widget tambahan yang Anda uji.
Uji alur autentikasi
- Di aplikasi, ketuk tombol RSVP untuk memulai
SignInScreen
.
- Masukan alamat email. Jika Anda sudah terdaftar, sistem akan meminta Anda memasukkan kata sandi. Jika tidak, sistem akan meminta Anda untuk mengisi formulir pendaftaran.
- Masukkan kata sandi yang kurang dari enam karakter untuk memeriksa alur penanganan kesalahan. Jika Anda terdaftar, Anda akan melihat kata sandinya.
- Masukkan kata sandi yang salah untuk memeriksa alur penanganan kesalahan.
- Masukkan kata sandi yang benar. Anda melihat pengalaman masuk, yang menawarkan pengguna kemampuan untuk keluar.
6. Tulis pesan ke Firestore
Sangat menyenangkan mengetahui bahwa pengguna akan datang, namun Anda perlu memberi tamu hal lain untuk dilakukan di aplikasi. Bagaimana jika mereka bisa meninggalkan pesan di buku tamu? Mereka dapat menceritakan alasan 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, bidang, dan subkoleksi. Anda menyimpan setiap pesan obrolan sebagai dokumen dalam koleksi guestbook
, yang merupakan koleksi tingkat atas.
Tambahkan pesan ke Firestore
Di bagian ini, Anda menambahkan fungsionalitas bagi pengguna untuk menulis pesan ke database. Pertama, Anda menambahkan kolom formulir dan tombol kirim, lalu Anda menambahkan kode yang menghubungkan elemen ini dengan database.
- Buat file baru bernama
guest_book.dart
, tambahkan widget statefulGuestBook
untuk membuat elemen UI bidang pesan dan tombol kirim:
lib/guest_book.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'src/widgets.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 hal menarik di sini. Pertama, Anda membuat instance formulir sehingga Anda bisa 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 lebih lanjut tentang Kunci dan cara menggunakannya, lihat Kapan Menggunakan Kunci .
Perhatikan juga cara tata letak widget, Anda memiliki Row
dengan TextFormField
dan StyledButton
, yang berisi Row
. Perhatikan juga bahwa TextFormField
dibungkus dalam widget Expanded
, yang memaksa TextFormField
untuk mengisi ruang tambahan di baris. Untuk lebih memahami mengapa hal ini diperlukan, lihat Memahami kendala .
Sekarang Anda memiliki widget yang memungkinkan pengguna memasukkan beberapa teks untuk ditambahkan ke Buku Tamu, Anda perlu menampilkannya di layar.
- Edit badan
HomePage
untuk menambahkan dua baris berikut di akhir turunanListView
:
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, namun tidak cukup untuk melakukan sesuatu yang berguna. Anda segera memperbarui kode ini agar berfungsi.
Pratinjau aplikasi
Saat pengguna mengklik KIRIM , cuplikan kode berikut akan dipicu. Itu menambahkan isi kolom input pesan ke koleksi guestbook
di 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/app_state.dart
, tambahkan metodeaddMessageToGuestBook
. Anda menghubungkan kemampuan ini dengan antarmuka pengguna pada langkah berikutnya.
lib/app_state.dart
import 'package:cloud_firestore/cloud_firestore.dart'; // new
import 'package:firebase_auth/firebase_auth.dart'
hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';
import 'firebase_options.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/home_page.dart
, lakukan perubahan berikut pada widgetHomePage
:
lib/home_page.dart
import 'package:firebase_auth/firebase_auth.dart'
hide EmailAuthProvider, PhoneAuthProvider;
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'app_state.dart';
import 'guest_book.dart'; // new
import 'src/authentication.dart';
import 'src/widgets.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
- Jika perlu, masuk ke aplikasi.
- 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 sebenarnya karena Anda masih perlu menerapkan pengambilan data, yang akan Anda lakukan pada langkah berikutnya. Namun, di dasbor Database Firebase console, 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:
7. Baca pesan
Sangat menyenangkan bahwa para tamu dapat menulis pesan ke database, tetapi mereka belum dapat melihatnya di aplikasi. Saatnya memperbaikinya!
Sinkronkan pesan
Untuk menampilkan pesan, Anda perlu menambahkan pendengar yang terpicu ketika data berubah dan kemudian membuat elemen UI yang menampilkan pesan baru. Anda menambahkan kode ke status aplikasi yang mendengarkan pesan yang baru ditambahkan dari aplikasi.
- Buat file baru
guest_book_message.dart
, tambahkan kelas berikut untuk menampilkan tampilan terstruktur dari data yang Anda simpan di Firestore.
lib/guest_book_message.dart
class GuestBookMessage {
GuestBookMessage({required this.name, required this.message});
final String name;
final String message;
}
- Di file
lib/app_state.dart
, tambahkan impor berikut:
lib/app_state.dart
import 'dart:async'; // new
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart'
hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';
import 'guest_book_message.dart'; // new
- Di bagian
ApplicationState
tempat Anda mendefinisikan status dan pengambil, tambahkan baris berikut:
lib/app_state.dart
bool _loggedIn = false;
bool get loggedIn => _loggedIn;
// Add from here...
StreamSubscription<QuerySnapshot>? _guestBookSubscription;
List<GuestBookMessage> _guestBookMessages = [];
List<GuestBookMessage> get guestBookMessages => _guestBookMessages;
// ...to here.
- Di bagian inisialisasi
ApplicationState
, tambahkan baris berikut untuk berlangganan kueri atas kumpulan dokumen saat pengguna masuk dan berhenti berlangganan saat mereka keluar:
lib/app_state.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 sinilah Anda membuat kueri atas koleksi guestbook
, dan menangani berlangganan dan berhenti berlangganan koleksi ini. Anda mendengarkan aliran tersebut, tempat Anda merekonstruksi cache lokal dari pesan-pesan dalam 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 di debugger untuk memeriksa apa yang terjadi untuk mendapatkan model mental yang lebih jelas. Untuk informasi selengkapnya, lihat Dapatkan pembaruan real-time dengan Firestore .
- Di file
lib/guest_book.dart
, tambahkan impor berikut:
import 'guest_book_message.dart';
- Di widget
GuestBook
, tambahkan daftar pesan sebagai bagian dari konfigurasi untuk menghubungkan perubahan status ini ke antarmuka pengguna:
lib/guest_book.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();
}
- Di
_GuestBookState
, ubah metodebuild
sebagai berikut untuk mengekspos konfigurasi ini:
lib/guest_book.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 menggabungkan konten metode build()
sebelumnya dengan widget Column
lalu menambahkan koleksi di bagian ekor turunan Column
untuk menghasilkan Paragraph
baru untuk setiap pesan dalam daftar pesan.
- Perbarui isi
HomePage
untuk membuatGuestBook
dengan benar menggunakan parametermessages
baru:
lib/home_page.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:
- Di aplikasi, temukan pesan yang Anda buat sebelumnya di database.
- Tulis pesan baru. Mereka muncul seketika.
- Buka ruang kerja Anda di beberapa jendela atau tab. Pesan-pesan tersebut disinkronkan secara real-time di seluruh jendela dan tab.
- Opsional: Di menu Database Firebase console, hapus, ubah, atau tambahkan pesan baru secara manual. Semua perubahan muncul di UI.
Selamat! Anda membaca dokumen Firestore di aplikasi Anda!
Pratinjau aplikasi
8. Tetapkan aturan keamanan dasar
Anda awalnya menyiapkan Firestore untuk menggunakan mode pengujian, yang berarti database Anda terbuka untuk membaca dan menulis. Namun, Anda sebaiknya hanya menggunakan mode uji coba pada tahap awal pengembangan. Sebagai praktik terbaik, Anda harus menyiapkan aturan keamanan untuk database saat Anda mengembangkan aplikasi. Keamanan merupakan bagian integral 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:
- Di menu Kembangkan di Firebase console, klik Database > Rules . Anda akan melihat aturan keamanan default berikut dan peringatan tentang aturan yang bersifat publik:
- 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.
}
}
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 tersebut memiliki UID Otentikasi yang cocok.
- 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;
}
}
}
Kini, hanya pengguna yang masuk yang dapat membaca pesan di buku tamu, namun hanya penulis pesan yang dapat mengedit pesan.
- 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 mengizinkan orang untuk ngobrol jika mereka tertarik dengan acara tersebut. Selain itu, satu-satunya cara Anda 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 bagi pengguna yang masuk untuk menentukan apakah mereka akan hadir. Yang kedua adalah penghitung berapa banyak orang yang hadir.
- Di file
lib/app_state.dart
, tambahkan baris berikut ke bagian pengakses diApplicationState
sehingga kode UI dapat berinteraksi dengan status ini:
lib/app_state.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/app_state.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) {
_loggedIn = true;
_emailVerified = user.emailVerified;
_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 {
_loggedIn = false;
_emailVerified = false;
_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 tersebut hadir.
- Tambahkan enumerasi berikut di bagian atas file
lib/app_state.dart
.
lib/app_state.dart
enum Attending { yes, no, unknown }
- Buat file baru
yes_no_selection.dart
, tentukan widget baru yang berfungsi seperti tombol radio:
lib/yes_no_selection.dart
import 'package:flutter/material.dart';
import 'app_state.dart';
import 'src/widgets.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: [
FilledButton(
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),
FilledButton(
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'),
),
],
),
);
}
}
}
Ini dimulai dalam keadaan tak tentu dengan tidak memilih Ya atau Tidak . Setelah pengguna memilih apakah mereka akan hadir, Anda menunjukkan opsi tersebut disorot dengan tombol terisi dan opsi lainnya menghilang dengan rendering datar.
- Perbarui metode
build()
HomePage
untuk memanfaatkanYesNoSelection
, memungkinkan pengguna yang masuk untuk menentukan apakah mereka akan hadir, dan menampilkan jumlah peserta acara:
lib/home_page.dart
Consumer<ApplicationState>(
builder: (context, appState, _) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Add from here...
switch (appState.attendees) {
1 => const Paragraph('1 person going'),
>= 2 => Paragraph('${appState.attendees} people going'),
_ => 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
.
- Di koleksi
attendees
, ambil UID Otentikasi yang Anda gunakan sebagai nama dokumen dan verifikasi bahwauid
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;
}
}
}
Hal ini memungkinkan semua orang membaca daftar peserta karena tidak ada data pribadi di sana, namun hanya pembuatnya yang dapat memperbaruinya.
- 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: Di aplikasi, klik tombol untuk melihat hasilnya di dasbor Firestore di Firebase console.
Pratinjau aplikasi
10. Selamat!
Anda menggunakan Firebase untuk membuat aplikasi web yang interaktif dan real-time!