Firestore Flutter Multiplatform

Sasaran

Dalam codelab ini, Anda akan mem-build aplikasi rekomendasi restoran multiplatform dan didukung oleh Flutter dan Cloud Firestore.

Aplikasi yang sudah selesai akan berjalan di Android, iOS, dan web, dari satu codebase Dart.

5e7215e72fa571b.png

Yang akan Anda pelajari

  • Membaca dan menulis data ke Cloud Firestore dari aplikasi Flutter
  • Memproses perubahan pada data Cloud Firestore secara real time
  • Menggunakan aturan keamanan dan Firebase Authentication untuk mengamankan data Cloud Firestore
  • Menulis transaksi dan kueri Cloud Firestore yang rumit

Apa yang ingin Anda pelajari dari codelab ini?

Saya baru mengenal topik ini, jadi saya ingin melihat ringkasan yang bagus. Saya sedikit paham soal topik ini, tetapi saya butuh penyegaran. Saya sedang mencari kode contoh untuk digunakan dalam project saya. Saya sedang mencari penjelasan tentang sesuatu yang spesifik.

Yang Anda perlukan

Jika Anda kurang memahami Flutter atau Firestore, selesaikan codelab Firebase untuk Flutter:

Untuk menyelesaikan codelab ini, Anda perlu:

  • IDE atau editor teks pilihan Anda, seperti Android Studio atau VS Code yang dikonfigurasikan dengan plugin Dart dan Flutter.
  • Browser Google Chrome, dan pemahaman terkait Chrome Dev Tools.
  • Versi terbaru Flutter (saluran beta atau yang lebih baru) dengan mengaktifkan dukungan web. Anda mengonfigurasi dukungan web selama codelab ini, tetapi untuk informasi selengkapnya, lihat halaman web support for Flutter.
  • Alat npm untuk menginstal alat command line firebase resmi untuk bagian terakhir codelab ini (Men-deploy indeks, Mengamankan data, dan Men-deploy ke Firebase Hosting).
  • (Opsional) Emulator atau perangkat Android yang terhubung, jika Anda ingin mengompilasi aplikasi untuk Android.
  • (Opsional) Mac dengan XCode yang memiliki versi yang cukup baru, jika Anda ingin mengompilasi aplikasi untuk iOS.

Membuat project Firebase

  1. Di Firebase console, klik Tambahkan project, lalu beri nama project Firebase dengan FriendlyEats. Jangan lupa dengan ID project untuk project Firebase Anda (atau klik ikon Edit untuk menetapkan ID project pilihan Anda).
  2. Klik Buat project.

Aplikasi yang Anda build menggunakan beberapa layanan Firebase yang tersedia di web:

  • Firebase Authentication untuk mengidentifikasi pengguna dengan mudah
  • Cloud Firestore untuk menyimpan data terstruktur di Cloud dan mendapatkan notifikasi instan saat data diperbarui
  • Firebase Hosting untuk menghosting dan menayangkan aset statis

Selanjutnya, Anda akan menjalani proses mengonfigurasi dan mengaktifkan layanan menggunakan Firebase console.

Mengaktifkan Autentikasi Anonim

Meskipun autentikasi bukanlah fokus dalam codelab ini, memiliki bentuk autentikasi di aplikasi merupakan hal yang penting. Anda akan menggunakan Login anonim—yang berarti pengguna login secara otomatis tanpa diminta.

Untuk mengaktifkan Login anonim:

  1. Di Firebase console, temukan bagian Develop di sisi kiri menu navigasi.
  2. Klik Authentication, lalu klik tab Metode login (atau langsung buka Firebase console).
  3. Aktifkan Penyedia Login Anonim, dan klik Simpan.

fee6c3ebdf904459.png

Mengaktifkan Login anonim memungkinkan aplikasi untuk membuat pengguna login secara otomatis saat mereka mengakses aplikasi web. Untuk mempelajari lebih lanjut, lihat dokumentasi autentikasi anonim.

Mengaktifkan Cloud Firestore

Aplikasi menggunakan Cloud Firestore untuk menyimpan dan menerima rating dan informasi restoran.

Untuk mengaktifkan Cloud Firestore:

  1. Di bagian Develop Firebase console, klik Database.
  2. Klik Buat database di panel Cloud Firestore.

57e83568e05c7710.png

  1. Pilih opsi Mulai dalam mode uji, lalu klik Aktifkan setelah membaca pernyataan penyangkalan tentang aturan keamanan.

Mode uji memastikan bahwa Anda dapat menulis database dengan bebas selama masa pengembangan. Anda dapat membuat database semakin aman di bagian akhir codelab ini.

daef1061fc25acc7.png

Clone repositori GitHub dari command line:

git clone https://github.com/FirebaseExtended/codelab-friendlyeats-flutter.git friendlyeats-flutter

Kode contoh harus di-clone ke direktori 📁friendlyeats-flutter. Mulai dari sekarang, pastikan Anda menjalankan perintah dari direktori ini:

cd friendlyeats-flutter

Mengimpor aplikasi awal

Buka atau impor direktori 📁friendlyeats-flutter ke IDE pilihan Anda. Direktori ini berisi kode awal untuk codelab yang terdiri dari aplikasi rekomendasi restoran yang belum berfungsi.

Anda dapat membuatnya berfungsi dengan mengikuti codelab ini, jadi Anda nanti akan mengedit kode di direktori tersebut.

Menemukan file yang harus dikerjakan

Meskipun titik entri yang biasanya untuk aplikasi Flutter ada di file lib/main.dart, dalam codelab ini, Anda berfokus pada bagian data.

Temukan file berikut di project:

  • lib/src/model/data.dart: File utama yang Anda ubah selama codelab ini. File berisi semua logika untuk membaca dan menulis data dari Firestore.
  • web/index.html: File yang dimuat browser untuk memulai aplikasi Anda. Anda mengubah file ini untuk menginstal dan melakukan inisialisasi pada library Firebase untuk web.

Firebase CLI

Antarmuka command line (CLI) Firebase memungkinkan Anda men-deploy langsung aplikasi web dan konfigurasi ke Firebase dari file di project Anda.

  1. Instal CLI dengan menjalankan perintah npm berikut:
npm -g install firebase-tools
  1. Pastikan bahwa CLI diinstal secara tepat dengan menjalankan perintah berikut:
firebase --version

Pastikan bahwa versi Firebase CLI v7.4.0 atau yang lebih baru.

  1. Beri otorisasi Firebase CLI dengan menjalankan perintah berikut:
firebase login

Repositori yang Anda clone di langkah sebelumnya sudah memiliki file firebase.json dengan konfigurasi project siap pakai (lokasi file konfigurasi lain, deployment hosting, dan sebagainya). Sekarang, Anda harus mengaitkan salinan aplikasi yang sudah berfungsi dengan project Firebase:

  1. Pastikan command line mengakses direktori lokal aplikasi.
  2. Jalankan perintah berikut untuk mengaitkan aplikasi dengan project Firebase:
firebase use --add
  1. Jika diminta, pilih Project ID, dan berikan alias untuk project Firebase Anda.

Alias berguna jika Anda memiliki beberapa lingkungan (produksi, staging, dan sebagainya). Namun, untuk codelab ini, cukup gunakan alias default.

  1. Ikuti petunjuk yang diberikan di command line.

Mengaktifkan dukungan web untuk Flutter

Untuk mengompilasi aplikasi Flutter guna menjalankannya di web, Anda harus mengaktifkan fitur ini (yang saat ini masih dalam versi beta). Untuk mengaktifkan dukungan web, masukkan yang berikut:

$ flutter channel beta
$ flutter upgrade
$ flutter config --enable-web

Pada menu pulldown perangkat di IDE, atau di command line menggunakan flutter devices, Anda sekarang akan melihat Chrome dan Server web dicantumkan.

Perangkat Chrome otomatis memulai Chrome. Server web memulai server yang menghosting aplikasi sehingga Anda dapat memuatnya dari browser mana pun.

Gunakan perangkat Chrome selama pengembangan sehingga Anda dapat menggunakan DevTools, lalu gunakan Server web saat Anda ingin mengujinya di browser lain.

Setelah membuat project Firebase, Anda dapat mengonfigurasi satu aplikasi (atau lebih) untuk menggunakan project Firebase tersebut. Lakukan hal berikut:

  • Daftarkan ID khusus platform aplikasi Anda dengan Firebase.
  • Buat file konfigurasi untuk aplikasi.
  • Tambahkan konfigurasi di tempat yang tepat di folder project Anda.

ac27fbbadff7a3b9.png

Jika mengembangkan aplikasi Flutter untuk beberapa platform, Anda harus mendaftarkan setiap platform tempat aplikasi dijalankan dalam project Firebase yang sama.

Codelab ini berfokus pada platform web karena iOS dan Android dibahas dalam codelab Firebase untuk Flutter. Buka codelab tersebut jika Anda ingin menambahkan dukungan untuk Android atau iOS ke aplikasi FriendlyEats.

Di aplikasi Flutter, ada web/index.html khusus yang digunakan sebagai titik entri ke aplikasi Anda saat menjalankan di web. Anda dapat mengubah titik entri dengan konfigurasi khusus untuk project, jadi aplikasi web dapat terhubung ke backend Firebase.

Mengonfigurasi untuk web

  1. Di Firebase console, pilih Ringkasan Project di menu navigasi sebelah kiri, lalu klik tombol Web di bawah Memulai dengan menambahkan Firebase ke aplikasi Anda. Anda akan melihat dialog berikut:

f76ed55f71f15953.png

  1. Beri nama panggilan untuk aplikasi Anda. Nilai ini digunakan di seluruh Firestore console untuk mengidentifikasi versi web dari aplikasi.
  2. Klik Daftarkan aplikasi.
  3. Setelah Anda mendaftarkan aplikasi, langkah Tambahkan Firebase SDK memberi Anda kode yang diperlukan untuk ditempel ke file web/index.html dari aplikasi Flutter Anda. Setelah selesai, kode akan terlihat seperti ini:

web/index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>friendlyeats</title>

  <!-- The core Firebase JS SDK is always required and must be listed first -->
  <script src="https://www.gstatic.com/firebasejs/7.5.1/firebase-app.js"></script>

  <!-- TODO: Add SDKs for Firebase products that you want to use
      https://firebase.google.com/docs/web/setup#available-libraries -->

  <script>
    // Your web app's Firebase configuration
    var firebaseConfig = {
      apiKey: "YoUr_RaNdOm_API_kEy",
      authDomain: "your-project-name.firebaseapp.com",
      databaseURL: "https://your-project-name.firebaseio.com",
      projectId: "your-project-name",
      storageBucket: "your-project-name.appspot.com",
      messagingSenderId: "012345678901",
      appId: "1:109876543210:web:r4nd0mH3xH45h"
    };
    // Initialize Firebase
    firebase.initializeApp(firebaseConfig);
  </script>

</head>
<body>
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
  1. Perhatikan bahwa ada TODO dalam kode yang baru Anda tempel. Anda kini sudah mengatasi masalahnya. Karena codelab menggunakan Firebase Auth dan Firestore, sekarang tambahkan tag skrip untuk produk tersebut:

web/index.html

  ...
  <!-- TODO: Add SDKs for Firebase products that you want to use
      https://firebase.google.com/docs/web/setup#available-libraries -->

  <script src="https://www.gstatic.com/firebasejs/7.5.1/firebase-auth.js"></script>
  <script src="https://www.gstatic.com/firebasejs/7.5.1/firebase-firestore.js"></script>

  <script>
    // Your web app's Firebase configuration
    var firebaseConfig = {
      ...
  1. Simpan file web/index.html, lalu klik Lanjutkan ke konsol di dialog Menambahkan Firebase ke aplikasi web Anda.
  2. Aplikasi Flutter siap terhubung ke Firebase.

d43a19dbf1248134.pngAnda telah menemukan hal yang istimewa!

Sebagian besar perubahan kode yang diperlukan untuk mengaktifkan dukungan Firebase sudah diperiksa di project yang sedang Anda kerjakan. Namun, guna menambahkan dukungan untuk platform seluler, Anda perlu mengikuti proses yang serupa dengan apa yang Anda jalani untuk web:

  • Daftarkan platform yang diinginkan di project Firebase
  • Download file konfigurasi khusus platform, lalu tambahkan file ke kode.

Di direktori level teratas aplikasi Flutter Anda, ada subdirektori bernama ios dan android. Direktori ini menyimpan masing-masing file konfigurasi khusus platform untuk iOS dan Android.

Mengonfigurasi iOS

  1. Di Firebase console, pilih Ringkasan Project di menu navigasi sebelah kiri, lalu klik tombol iOS di bawah Memulai dengan menambahkan Firebase ke aplikasi Anda.

Anda akan melihat dialog berikut:

c42139f18fb9a2ee.png

  1. Nilai penting yang harus diberikan adalah ID paket iOS. Anda dapat memperoleh ID paket dengan menjalankan tiga langkah berikut.
  1. Di alat command-line, buka direktori level teratas aplikasi Flutter Anda.
  2. Jalankan perintah open ios/Runner.xcworkspace untuk membuka Xcode.
  1. Di Xcode, klik Runner level teratas di panel sebelah kiri untuk menampilkan tab General di panel sebelah kanan seperti yang ditunjukkan pada gambar di bawah ini. Salin nilai ID Paket.

9733e26be329f329.png

  1. Kembali ke dialog Firebase, tempel ID Paket yang disalin ke kolom ID paket iOS, lalu klik Daftarkan Aplikasi.
  1. Lanjutkan di Firebase, lalu ikuti petunjuk untuk mendownload file konfigurasi GoogleService-Info.plist.
  2. Kembali ke Xcode. Perhatikan bahwa Runner memiliki subfolder yang juga bernama Runner (ditunjukkan di gambar sebelumnya).
  3. Seret file GoogleService-Info.plist (yang baru Anda download) ke subfolder Runner.
  4. Di dialog yang muncul di Xcode, klik Finish.
  5. Kembali ke Firebase console. Pada langkah penyiapan, klik Berikutnya, lewati langkah-langkah yang tersisa, lalu kembali ke halaman utama Firebase console.

Anda telah selesai mengonfigurasi aplikasi Flutter Anda untuk iOS.

Mengonfigurasi Android

  1. Di Firebase Console, pilih Ringkasan Project di menu navigasi sebelah kiri, lalu klik tombol Android di bawah Memulai dengan menambahkan Firebase ke aplikasi Anda.

Anda akan melihat dialog berikut: 8254fc299e82f528.png

  1. Nilai penting yang harus diberikan adalah Nama paket Android. Anda akan mendapatkan nama paket saat Anda menjalankan dua langkah berikut:
  1. Di direktori aplikasi Flutter Anda, buka file android/app/src/main/AndroidManifest.xml.
  2. Di elemen manifest, temukan nilai string atribut package. Nilai ini adalah nama paket Android (misalnya seperti com.yourcompany.yourproject). Salin nilai ini.
  3. Di dialog Firebase, tempel nama paket yang disalin ke kolom Nama paket Android.
  4. Anda tidak perlu Sertifikat penandatanganan debug SHA-1 untuk codelab ini. Anda tidak perlu mengisinya.
  5. Klik Daftarkan Aplikasi.
  6. Lanjutkan di Firebase, lalu ikuti petunjuk untuk mendownload file konfigurasi google-services.json.
  7. Buka direktori aplikasi Flutter Anda, lalu pindahkan file google-services.json (yang baru saja Anda download) ke direktori android/app.
  8. Kembali di Firebase console, lewati langkah-langkah yang tersisa, lalu kembali ke halaman utama Firebase console.
  9. Semua konfigurasi Gradle sudah diperiksa. Jika aplikasi Anda masih berjalan, tutup lalu build ulang untuk memungkinkan gradle menginstal dependensi.

Anda telah selesai mengonfigurasi aplikasi Flutter Anda untuk Android.

Anda sudah siap untuk membuat aplikasi Anda sendiri. Pertama, jalankan aplikasi secara lokal. Anda kini dapat menjalankan aplikasi di platform mana pun yang Anda konfigurasi (serta di emulator dan perangkat Anda yang tersedia).

Temukan perangkat mana yang tersedia dengan perintah berikut:

flutter devices

Bergantung perangkat yang tersedia, output perintah sebelumnya terlihat seperti yang berikut ini:

3 connected devices:

Android SDK built for x86 • emulator-5554 • android-x86    • Android 7.1.1 (API 25) (emulator)
Chrome                    • chrome        • web-javascript • Google Chrome 79.0.3945.130
Web Server                • web-server    • web-javascript • Flutter Tools

Kita akan melanjutkan codelab ini menggunakan perangkat chrome.

  1. Jalankan perintah Flutter CLI berikut:
flutter run -d chrome
  1. Flutter dimulai dengan Membuat aplikasi Anda untuk web..., dan otomatis membuka jendela Chrome dengan aplikasi yang sedang berjalan.

Sekarang, Anda akan melihat salinan FriendlyEats terhubung ke project Firebase.

Aplikasi menghubungkan ke project Firebase secara otomatis dan membuat Anda login otomatis sebagai pengguna anonim.

c45806a2ac9300d9.png

Di bagian ini, Anda menulis data ke Cloud Firestore untuk mengisi UI aplikasi. Tindakan ini dapat diselesaikan secara manual menggunakan Firebase console, tetapi Anda akan melakukannya di aplikasi untuk melihat demonstrasi penulisan Cloud Firestore dasar.

Model Data

Data Firestore dibagi menjadi beberapa bagian: koleksi, dokumentasi, kolom, dan subkoleksi. Setiap restoran disimpan sebagai dokumen di koleksi level teratas yang bernama restaurants.

92f8dc2c769d2d6c.png

Kemudian, Anda menyimpan tiap ulasan di subkoleksi yang bernama ratings di dalam setiap restaurant.

a00d9eb006ddd6c0.png

Menambahkan restoran ke Firestore

Objek model utama di aplikasi adalah restoran. Berikutnya, tulis kode yang menambahkan dokumen restoran ke koleksi restaurants.

  1. Buka lib/src/model/data.dart
  2. Temukan fungsi addRestaurant.
  3. Ganti seluruh fungsi dengan kode berikut.

lib/src/model/data.dart

Future<void> addRestaurant(Restaurant restaurant) {
  final restaurants = FirebaseFirestore.instance.collection('restaurants');
  return restaurants.add({
    'avgRating': restaurant.avgRating,
    'category': restaurant.category,
    'city': restaurant.city,
    'name': restaurant.name,
    'numRatings': restaurant.numRatings,
    'photo': restaurant.photo,
    'price': restaurant.price,
  });
}

Kode sebelumnya menambahkan dokumen baru ke koleksi restaurants.

Anda melakukannya dengan mendapatkan referensi terlebih dahulu ke koleksi Cloud Firestore restaurants, lalu adding data.

Data dokumen berasal dari objek Restaurant, yang perlu dikonversi ke Map untuk plugin Firestore.

Menambahkan beberapa restoran

  1. Build ulang dan muat ulang Aplikasi Flutter Anda (Shift + R di jendela terminal yang menjalankan aplikasi Anda).
  2. Klik ADD SOME.

Aplikasi otomatis membuat kumpulan objek restaurants acak, dan memanggil addRestaurant Anda. Namun, Anda tidak akan melihat data di aplikasi web karena Anda masih harus mengimplementasikan pengambilan data (bagian codelab yang berikutnya).

Jika membuka tab Develop > Database > Cloud Firestore di Firebase console, Anda akan membuka dokumen baru di koleksi restaurants.

f06898b9d6dd4881.png

Selamat, Anda baru saja menulis data ke Cloud Firestore dari aplikasi web.

Di bagian selanjutnya Anda akan mempelajari cara mengambil data dari Cloud Firestore dan menampilkannya di aplikasi.

Di bagian ini, Anda akan mempelajari cara mengambil data dari Cloud Firestore dan menampilkannya di aplikasi. Dua langkah utama dalam bagian ini adalah membuat kueri dan memproses di Stream snapshot. Pemroses ini mendapatkan notifikasi dari semua data yang ada yang cocok dengan kueri dan menerima pembaruan secara real time.

Pertama, buat kueri yang menayangkan daftar restoran yang default dan tidak difilter.

  1. Kembali ke file lib/src/model/data.dart.
  2. Temukan fungsi loadAllRestaurants.
  3. Ganti seluruh fungsi dengan kode berikut.

lib/src/model/data.dart

Stream<QuerySnapshot> loadAllRestaurants() {
  return FirebaseFirestore.instance
      .collection('restaurants')
      .orderBy('avgRating', descending: true)
      .limit(50)
      .snapshots();
}

Kode sebelumnya membuat kueri yang mengambil sampai 50 restoran dari koleksi level teratas yang bernama restaurants, diurutkan berdasarkan rating rata-rata (saat ini semua rata-rata masih nol).

Sekarang, Anda harus mengubah setiap QuerySnapshot yang ditampilkan dari Stream ke data Restaurant yang dapat Anda render.

Untuk mengekstrak informasi Restaurant dari QuerySnapshot dari koleksi restaurants:

  1. Kembali ke file lib/src/model/data.dart.
  2. Temukan fungsi getRestaurantsFromQuery.
  3. Ganti seluruh fungsi dengan kode berikut:

lib/src/model/data.dart

List<Restaurant> getRestaurantsFromQuery(QuerySnapshot snapshot) {
  return snapshot.docs.map((DocumentSnapshot doc) {
    return Restaurant.fromSnapshot(doc);
  }).toList();
}

Metode getRestaurantsFromQuery dipanggil setiap kali ada QuerySnapshot baru dari Query yang Anda buat sebelumnya. QuerySnapshots adalah mekanisme yang digunakan Firestore untuk memberitahukan perubahan aplikasi Anda ke Query secara real time.

Metode ini hanya mengonversi semua documents yang berada di snapshot menjadi objek Restaurant yang dapat digunakan di tempat lain di aplikasi Flutter Anda.

Karena Anda sekarang sudah mengimplementasikan kedua metode, build ulang dan muat ulang aplikasi, lalu pastikan bahwa restoran yang Anda lihat sebelumnya di Firebase console terlihat di aplikasi. Jika Anda berhasil menjalankan langkah-langkah di bagian ini, aplikasi Anda akan membaca dan menulis data dengan Cloud Firestore.

Karena daftar restoran berubah sewaktu-waktu, pemroses ini akan memperbarui secara otomatis. Coba buka Firebase console dan hapus restoran atau ubah namanya secara manual—nanti Anda akan segera melihat perubahan yang ditampilkan di situs.

edd9adbafa5bd539.png

Sejauh ini, Anda telah mempelajari cara menggunakan onSnapshot untuk mengambil pembaruan secara real time; tetapi, langkah tersebut bukanlah langkah yang selalu Anda inginkan. Terkadang, kita hanya perlu mengambil data sekali.

Anda memerlukan metode yang memuat restoran tertentu dari ID-nya, saat pengguna mengklik restoran tertentu di aplikasi.

  1. Kembali ke file lib/src/model/data.dart Anda.
  2. Temukan fungsi getRestaurant.
  3. Ganti seluruh fungsi dengan kode berikut:

lib/src/model/data.dart

Future<Restaurant> getRestaurant(String restaurantId) {
  return FirebaseFirestore.instance
      .collection('restaurants')
      .doc(restaurantId)
      .get()
      .then((DocumentSnapshot doc) => Restaurant.fromSnapshot(doc));
}

Kode menggunakan get() untuk mengambil Future<DocumentSnapshot> yang berisi informasi restoran yang Anda minta. Cukup salurkan kode tersebut melalui then() ke fungsi yang mengonversi DocumentSnapshot menjadi objek Restaurant kapan pun kode siap.

Setelah mengimplementasikan metode ini, Anda dapat melihat setiap halaman detail restoran.

  1. Muat ulang aplikasi Anda dengan menekan Shift + R di terminal tempat flutter dijalankan.
  2. Klik restoran di daftar, dan Anda akan melihat halaman detail restoran:

f8ca540dda5540a9.png

Selanjutnya, tambahkan kode yang Anda perlukan untuk menambahkan rating ke restoran menggunakan transaksi.

Di bagian ini, Anda menambahkan kemampuan bagi pengguna untuk mengirim ulasan ke restoran. Sejauh ini, semua penulisan Anda bersifat atomik dan relatif sederhana. Jika ada penulisan yang error, Anda kemungkinan cukup meminta pengguna mencoba ulang penulisan, atau aplikasi Anda akan mencoba ulang penulisan secara otomatis.

Aplikasi Anda akan memiliki banyak pengguna yang ingin menambahkan rating untuk restoran, jadi Anda harus mengoordinasikan beberapa pembacaan dan penulisan. Pertama, ulasan harus dikirimkan, lalu rating count dan average rating harus diperbarui. Jika ada satu yang gagal, tetapi tidak dengan yang lain, berarti status Anda tetap tidak konsisten. Data pada satu bagian database tidak cocok dengan data di tempat lain.

Untungnya, Cloud Firestore menyediakan fungsi transaksi yang memungkinkan Anda menjalankan beberapa pembacaan dan penulisan dalam operasi atomik tunggal, memastikan bahwa data Anda tetap konsisten.

  1. Kembali ke file lib/src/model/data.dart Anda.
  2. Temukan fungsi addReview.
  3. Ganti seluruh fungsi dengan kode berikut:

lib/src/model/data.dart

Future<void> addReview({String restaurantId, Review review}) {
  final restaurant =
      FirebaseFirestore.instance.collection('restaurants').doc(restaurantId);
  final newReview = restaurant.collection('ratings').doc();

  return FirebaseFirestore.instance.runTransaction((Transaction transaction) {
    return transaction
        .get(restaurant)
        .then((DocumentSnapshot doc) => Restaurant.fromSnapshot(doc))
        .then((Restaurant fresh) {
      final newRatings = fresh.numRatings + 1;
      final newAverage =
          ((fresh.numRatings * fresh.avgRating) + review.rating) / newRatings;

      transaction.update(restaurant, {
        'numRatings': newRatings,
        'avgRating': newAverage,
      });

      transaction.set(newReview, {
        'rating': review.rating,
        'text': review.text,
        'userName': review.userName,
        'timestamp': review.timestamp ?? FieldValue.serverTimestamp(),
        'userId': review.userId,
      });
    });
  });
}

Fungsi sebelumnya memicu transaksi yang memulai dengan mengambil versi fresh dari Restaurant yang diwakili oleh restaurantId.

Lalu, Anda memperbarui nilai numerik avgRating dan numRatings di referensi dokumen restaurant.

Di saat yang sama, Anda menambahkan review yang baru melalui referensi dokumen newReview ke subkoleksi ratings dari restoran.

Untuk menguji kode yang baru saja Anda tambahkan:

  1. Muat ulang aplikasi Anda dengan menekan Shift + R di terminal tempat flutter dijalankan.
  2. Buka halaman detail Restoran.
  3. Tambahkan beberapa ulasan. Anda dapat melakukannya dengan:
  • Mengklik tombol ADD SOME di daftar yang kosong, jika tidak ada ulasan
  • Mengklik Tombol tindakan mengambang (FAB) +, lalu mengetik ulasan Anda sendiri

Saat ini, aplikasi menampilkan daftar restoran, tetapi pengguna tidak memiliki cara untuk memfilter restoran berdasarkan kebutuhan mereka. Di bagian ini, Anda menggunakan pembuatan kueri tingkat lanjut Cloud Firestore untuk mengaktifkan pemfilteran.

Berikut adalah contoh kueri sederhana untuk mengambil semua restoran Dim Sum:

Query filteredCollection = FirebaseFirestore.instance
        .collection('restaurants')
        .where('category', isEqualTo: 'Dim Sum');

Seperti namanya, metode where() membuat kueri mendownload anggota koleksi yang kolomnya memenuhi batasan yang Anda tetapkan saja. Dalam hal ini, metode hanya mendownload restoran di mana category setara dengan Dim Sum.

Demikian pula, Anda dapat mengurutkan data yang ditampilkan:

Query filteredAndSortedCollection = FirebaseFirestore.instance
        .collection('restaurants')
        .where('category', isEqualTo: 'Dim Sum')
        .orderBy('price', descending: true);

Metode orderBy() membuat kueri mengurutkan restoran Dim Sum menurut atribut price di restoran, mulai dari harga paling mahal hingga yang paling murah.

Di aplikasi, pengguna dapat merangkai beberapa filter untuk membuat kueri tertentu, seperti Rawon di Bandung atau Soto di Jakarta, yang diurutkan berdasarkan popularitas.

Anda menciptakan metode yang membuat kueri yang memfilter restoran berdasarkan beberapa kriteria yang dipilih oleh pengguna.

  1. Kembali ke file lib/src/model/data.dart Anda.
  2. Temukan fungsi loadFilteredRestaurants.
  3. Ganti seluruh fungsi dengan kode berikut:

lib/src/model/data.dart

Stream<QuerySnapshot> loadFilteredRestaurants(Filter filter) {
  Query collection = FirebaseFirestore.instance.collection('restaurants');
  if (filter.category != null) {
    collection = collection.where('category', isEqualTo: filter.category);
  }
  if (filter.city != null) {
    collection = collection.where('city', isEqualTo: filter.city);
  }
  if (filter.price != null) {
    collection = collection.where('price', isEqualTo: filter.price);
  }
  return collection
      .orderBy(filter.sort ?? 'avgRating', descending: true)
      .limit(50)
      .snapshots();
}

Kode sebelumnya menambahkan beberapa filter where dan satu klausa orderBy untuk membuat kueri gabungan berdasarkan input pengguna. Sekarang, kueri hanya menampilkan restoran yang cocok dengan persyaratan pengguna.

Muat ulang aplikasi Anda di browser dengan menekan Shift + R di terminal tempat flutter dijalankan.

Sekarang, coba filter berdasarkan harga, kota, dan kategori. Saat menguji, Anda akan melihat error di Konsol JavaScript dari browser yang terlihat seperti ini:

The query requires an index. You can create it here: https://console.firebase.google.com/project/.../database/firestore/indexes?create_index=...

Error tersebut terjadi karena Cloud Firestore memerlukan indeks untuk sebagian besar kueri gabungan. Memerlukan indeks untuk kueri mempertahankan kecepatan tinggi Cloud Firestore dalam skala besar.

Membuka link dari pesan error otomatis membuka UI pembuatan indeks di Firebase console dengan parameter yang diisi dengan benar.

Di bagian selanjutnya, Anda menulis dan men-deploy indeks yang diperlukan untuk aplikasi ini, semua sekaligus, dari Firebase CLI.

Jika tidak ingin menjelajahi setiap jalur di aplikasi dan mengikuti setiap link pembuatan indeks, Anda dapat dengan mudah men-deploy banyak indeks sekaligus menggunakan Firebase CLI.

  1. Di root project aplikasi Anda, temukan file firestore.indexes.json.

File ini menjelaskan semua indeks yang diperlukan untuk semua kemungkinan kombinasi filter.

firestore.indexes.json

{
 "indexes": [
   {
     "collectionGroup": "restaurants",
     "queryScope": "COLLECTION",
     "fields": [
       { "fieldPath": "city", "order": "ASCENDING" },
       { "fieldPath": "avgRating", "order": "DESCENDING" }
     ]
   },

   ...

 ]
}
  1. Deploy indeks tersebut dengan perintah berikut:
firebase deploy --only firestore:indexes

Setelah beberapa menit, indeks Anda aktif, dan pesan error akan hilang. Jika mencoba menggunakan indeks sebelum indeks siap sepenuhnya, Anda mungkin melihat error yang serupa dengan yang berikut:

The query requires an index. That index is currently building and cannot be used yet. See its status here:
https://console.firebase.google.com/project/.../database/firestore/indexes?create_index=...

Di bagian awal codelab ini, Anda menetapkan aturan keamanan aplikasi Anda guna membuka database sepenuhnya untuk pembacaan atau penulisan apa pun. Dalam penerapan nyata, Anda akan menetapkan aturan yang lebih akurat untuk mencegah pengubahan atau akses data yang tidak diinginkan

.

  1. Di bagian Develop Firebase console, klik Database.
  2. Klik Aturan di bagian Cloud Firestore (atau langsung buka Firebase console).
  3. Ganti default dengan aturan berikut, lalu klik Publikasikan.

firestore.rules

service cloud.firestore {
  match /databases/{database}/documents {
    // Restaurants:
    //   - Authenticated user can read
    //   - Authenticated user can create/update (for demo)
    //   - Validate updates
    //   - Deletes are not allowed
    match /restaurants/{restaurantId} {
      allow read, create: if request.auth != null;
      allow update: if request.auth != null
                    && request.resource.data.name == resource.data.name
      allow delete: if false;

      // Ratings:
      //   - Authenticated user can read
      //   - Authenticated user can create if userId matches
      //   - Deletes and updates are not allowed
      match /ratings/{ratingId} {
        allow read: if request.auth != null;
        allow create: if request.auth != null
                      && request.resource.data.userId == request.auth.uid;
        allow update, delete: if false;
      }
    }
  }
}

Aturan tersebut membatasi akses untuk memastikan bahwa klien hanya membuat perubahan yang aman, misalnya:

  • Pembaruan pada dokumen restoran hanya dapat mengubah rating, bukan nama atau data lain yang sifatnya tidak dapat diubah.
  • Rating hanya dapat dibuat jika ID pengguna cocok dengan pengguna yang login demi mencegah spoofing.

Daripada menggunakan Firebase console, Anda dapat menggunakan Firebase CLI untuk men-deploy aturan ke project Firebase Anda. File firestore.rules di direktori kerja saat ini sudah berisi aturan sebelumnya. Untuk men-deploy aturan tersebut dari sistem file lokal Anda (daripada menggunakan Firebase console), jalankan perintah berikut:

firebase deploy --only firestore:rules

flutter build web

Sejauh ini, Anda hanya menggunakan versi "debug" dari aplikasi Flutter Anda. Build tersebut lebih lambat karena build berisi informasi tambahan untuk membuat proses debug lebih mudah.

Sebelum men-deploy aplikasi, Anda harus mem-build versi produksi (prod). Flutter memungkinkan Anda mem-build untuk produksi dengan alat build:

flutter build web

Tindakan ini menempatkan semua aset build produksi ke direktori build/web dari project.

Sekarang, aplikasi siap di-deploy ke Firebase.

firebase deploy

Project Anda sudah dikonfigurasi sebelumnya untuk mem-build dan men-deploy aset yang Flutter buat (lihat file firebase.json di root project).

Deploy versi baru aplikasi Anda dengan Firebase menggunakan perintah berikut:

firebase init hosting
firebase deploy --only hosting

Proses sebelumnya hanya akan memakan waktu beberapa detik. Proses tersebut membersihkan build sebelumnya (flutter clean), mem-build ulang aplikasi Anda (flutter build web), dan men-deploy aset yang baru di-build (isi dari build/web) ke Firebase Hosting.

Pesan sukses berisi URL Hosting tempat aplikasi Anda yang dipublikasi kini tersedia di internet.

Selamat.

Dalam codelab ini, Anda telah mempelajari cara menghubungkan aplikasi web Flutter Anda ke Firebase menggunakan plugin Firestore dan Firebase Auth, menjalankan pembacaan dan penulisan tingkat lanjut dengan Cloud Firestore, dan mengamankan akses data dengan aturan keamanan.

Anda dapat menemukan solusi lengkap di cabang done repositori.

Untuk mempelajari lebih lanjut Dart dan Flutter, lihat di situs resmi masing-masing:

Untuk mempelajari Cloud Firestore lebih lanjut, buka referensi berikut:

Codelab ini merupakan titik awal yang baik untuk mempelajari fitur Firestore (dan Firebase) lain. Jika ingin tantangan lain, Anda dapat mencoba untuk:

  • Menggunakan batch operasi tulis di metode addRestaurantsBatch untuk menambahkan semua restoran dan ulasan dalam satu permintaan sehingga UI aplikasi Anda hanya memperbarui sekali.
  • Mengubah widget RestaurantAppBar sehingga widget memperbarui rating bintang restoran secara real time saat pengguna menambahkan ulasan ke widget.
  • Mengaktifkan firebase_auth dengan google_sign_in untuk mengambil nama depan asli milik pengguna yang memposting ulasan karena semua pengguna saat ini adalah pengguna anonim.