Phát triển cục bộ cho các ứng dụng Flutter của bạn bằng Bộ mô phỏng Firebase

1. Trước khi bạn bắt đầu

Trong lớp học lập trình này, bạn sẽ tìm hiểu cách sử dụng Bộ mô phỏng Firebase với Flutter trong quá trình phát triển cục bộ. Bạn sẽ tìm hiểu cách sử dụng xác thực mật khẩu email thông qua Bộ mô phỏng cũng như cách đọc và ghi dữ liệu vào trình mô phỏng Firestore. Cuối cùng, bạn sẽ làm việc với việc nhập và xuất dữ liệu từ trình mô phỏng để làm việc với cùng một dữ liệu giả mạo mỗi khi bạn quay lại quá trình phát triển.

Điều kiện tiên quyết

Lớp học lập trình này giả định rằng bạn đã có một số kinh nghiệm về Flutter. Nếu không, trước tiên bạn có thể muốn tìm hiểu những điều cơ bản. Các liên kết sau đây rất hữu ích:

Bạn cũng nên có một số kinh nghiệm về Firebase, nhưng sẽ không sao nếu bạn chưa bao giờ thêm Firebase vào dự án Flutter. Nếu bạn chưa quen với bảng điều khiển Firebase hoặc bạn hoàn toàn mới làm quen với Firebase, trước tiên hãy xem các liên kết sau:

Những gì bạn sẽ tạo ra

Lớp học lập trình này hướng dẫn bạn cách xây dựng một ứng dụng Ghi nhật ký đơn giản. Ứng dụng sẽ có màn hình đăng nhập và màn hình cho phép bạn đọc các mục nhật ký trước đây và tạo các mục mới.

cd5c4753bbee8af.png8cb4d21f656540bf.png

Bạn sẽ học được gì

Bạn sẽ tìm hiểu cách bắt đầu sử dụng Firebase cũng như cách tích hợp và sử dụng bộ Trình mô phỏng Firebase vào quy trình phát triển Flutter của mình. Các chủ đề Firebase này sẽ được đề cập:

Lưu ý rằng các chủ đề này được đề cập trong chừng mực chúng bắt buộc phải có trong bộ mô phỏng Firebase. Lớp học lập trình này tập trung vào việc thêm dự án Firebase vào ứng dụng Flutter của bạn và phát triển bằng Bộ mô phỏng Firebase. Sẽ không có các cuộc thảo luận chuyên sâu về Xác thực Firebase hoặc Firestore. Nếu bạn chưa quen với những chủ đề này, chúng tôi khuyên bạn nên bắt đầu với lớp học lập trình Làm quen với Firebase cho Flutter .

Những gì bạn cần

  • Kiến thức làm việc về Flutter và cài đặt SDK
  • Trình soạn thảo văn bản Intellij JetBrains hoặc VS Code
  • Trình duyệt Google Chrome (hoặc mục tiêu phát triển ưa thích khác của bạn cho Flutter. Một số lệnh đầu cuối trong lớp học lập trình này sẽ cho rằng bạn đang chạy ứng dụng của mình trên Chrome)

2. Tạo và thiết lập dự án Firebase

Nhiệm vụ đầu tiên bạn cần hoàn thành là tạo dự án Firebase trong bảng điều khiển web của Firebase. Phần lớn lớp học lập trình này sẽ tập trung vào Emulator Suite, sử dụng giao diện người dùng chạy cục bộ, nhưng trước tiên bạn phải thiết lập một dự án Firebase đầy đủ.

Tạo dự án Firebase

  1. Đăng nhập vào bảng điều khiển Firebase.
  2. Trong bảng điều khiển Firebase, hãy nhấp vào Thêm dự án (hoặc Tạo dự án ) và nhập tên cho dự án Firebase của bạn (ví dụ: " Firebase-Flutter-Codelab") .

fe6aeab3b91965ed.png

  1. Nhấp qua các tùy chọn tạo dự án. Chấp nhận các điều khoản của Firebase nếu được nhắc. Bỏ qua việc thiết lập Google Analytics vì bạn sẽ không sử dụng Analytics cho ứng dụng này.

d1fcec48bf251eaa.png

Để tìm hiểu thêm về các dự án Firebase, hãy xem Tìm hiểu các dự án Firebase .

Ứng dụng bạn đang xây dựng sử dụng hai sản phẩm Firebase có sẵn cho ứng dụng Flutter:

  • Xác thực Firebase để cho phép người dùng đăng nhập vào ứng dụng của bạn.
  • Cloud Firestore để lưu dữ liệu có cấu trúc trên đám mây và nhận thông báo tức thì khi dữ liệu thay đổi.

Hai sản phẩm này cần có cấu hình đặc biệt hoặc cần được kích hoạt bằng bảng điều khiển Firebase.

Kích hoạt Cloud Firestore

Ứng dụng Flutter sử dụng Cloud Firestore để lưu các mục nhật ký.

Kích hoạt Cloud Firestore:

  1. Trong phần Build của bảng điều khiển Firebase, nhấp vào Cloud Firestore .
  2. Nhấp vào Tạo cơ sở dữ liệu . 99e8429832d23fa3.png
  3. Chọn tùy chọn Bắt đầu ở chế độ thử nghiệm . Đọc tuyên bố từ chối trách nhiệm về các quy tắc bảo mật. Chế độ kiểm tra đảm bảo rằng bạn có thể tự do ghi vào cơ sở dữ liệu trong quá trình phát triển. Bấm tiếp . 6be00e26c72ea032.png
  4. Chọn vị trí cho cơ sở dữ liệu của bạn (Bạn chỉ có thể sử dụng mặc định). Lưu ý rằng vị trí này không thể thay đổi sau này. 278656eefcfb0216.png
  5. Nhấp vào Bật .

3. Thiết lập ứng dụng Flutter

Bạn sẽ cần tải xuống mã khởi động và cài đặt Firebase CLI trước khi chúng ta bắt đầu.

Lấy mã khởi đầu

Sao chép kho GitHub từ dòng lệnh:

git clone https://github.com/flutter/codelabs.git flutter-codelabs

Ngoài ra, nếu bạn đã cài đặt công cụ cli của GitHub :

gh repo clone flutter/codelabs flutter-codelabs

Mã mẫu phải được sao chép vào thư mục flutter-codelabs , nơi chứa mã cho một tập hợp các lớp học lập trình. Mã cho lớp học lập trình này nằm trong flutter-codelabs/firebase-emulator-suite .

Cấu trúc thư mục trong flutter-codelabs/firebase-emulator-suite là hai dự án Flutter. Một cái được gọi là complete , bạn có thể tham khảo nếu muốn bỏ qua phần tiếp theo hoặc tham chiếu chéo mã của riêng bạn. Dự án khác được gọi là start .

Mã bạn muốn bắt đầu nằm trong thư mục flutter-codelabs/firebase-emulator-suite/start . Mở hoặc nhập thư mục đó vào IDE ưa thích của bạn.

cd flutter-codelabs/firebase-emulator-suite/start

Cài đặt Firebase CLI

Firebase CLI cung cấp các công cụ để quản lý các dự án Firebase của bạn. Cần có CLI để sử dụng Bộ mô phỏng, vì vậy bạn sẽ cần phải cài đặt nó.

Có nhiều cách khác nhau để cài đặt CLI. Cách đơn giản nhất, nếu bạn đang sử dụng MacOS hoặc Linux, là chạy lệnh này từ thiết bị đầu cuối của mình:

curl -sL https://firebase.tools | bash

Sau khi cài đặt CLI, bạn phải xác thực bằng Firebase.

  1. Đăng nhập vào Firebase bằng tài khoản Google của bạn bằng cách chạy lệnh sau:
firebase login
  1. Lệnh này kết nối máy cục bộ của bạn với Firebase và cấp cho bạn quyền truy cập vào các dự án Firebase của mình.
  1. Kiểm tra xem CLI đã được cài đặt đúng cách và có quyền truy cập vào tài khoản của bạn hay chưa bằng cách liệt kê các dự án Firebase của bạn. Chạy lệnh sau:
firebase projects:list
  1. Danh sách được hiển thị phải giống với danh sách các dự án Firebase được liệt kê trong bảng điều khiển Firebase . Ít nhất bạn nên xem firebase-flutter-codelab.

Cài đặt FlutterFire CLI

FlutterFire CLI được xây dựng dựa trên Firebase CLI và giúp việc tích hợp dự án Firebase với ứng dụng Flutter của bạn dễ dàng hơn.

Đầu tiên, cài đặt CLI:

dart pub global activate flutterfire_cli

Đảm bảo CLI đã được cài đặt. Chạy lệnh sau trong thư mục dự án Flutter và đảm bảo rằng CLI xuất ra menu trợ giúp.

flutterfire --help

Sử dụng Firebase CLI và FlutterFire CLI để thêm dự án Firebase vào ứng dụng Flutter của bạn

Với hai CLI được cài đặt, bạn có thể thiết lập các sản phẩm Firebase riêng lẻ (như Firestore), tải xuống trình mô phỏng và thêm Firebase vào ứng dụng Flutter của mình chỉ bằng một vài lệnh đầu cuối.

Trước tiên, hãy hoàn tất thiết lập Firebase bằng cách chạy như sau:

firebase init

Lệnh này sẽ dẫn bạn qua một loạt câu hỏi cần thiết để thiết lập dự án của bạn. Những ảnh chụp màn hình này hiển thị dòng chảy:

  1. Khi được nhắc chọn các tính năng, hãy chọn "Firestore" và "Trình giả lập". (Không có tùy chọn Xác thực vì nó không sử dụng cấu hình có thể sửa đổi từ các tệp dự án Flutter của bạn.) fe6401d769be8f53.png
  2. Tiếp theo, chọn "Sử dụng dự án hiện có" khi được nhắc.

f11dcab439e6ac1e.png

  1. Bây giờ, hãy chọn dự án bạn đã tạo ở bước trước: Flutter-firebase-codelab.

3bdc0c6934991c25.png

  1. Tiếp theo, bạn sẽ được hỏi một loạt câu hỏi về cách đặt tên tệp sẽ được tạo. Tôi khuyên bạn nên nhấn "enter" cho mỗi câu hỏi để chọn mặc định. 9bfa2d507e199c59.png
  2. Cuối cùng, bạn sẽ cần định cấu hình trình mô phỏng. Chọn Firestore và Xác thực từ danh sách, sau đó nhấn "Enter" cho từng câu hỏi về các cổng cụ thể sẽ sử dụng cho từng trình mô phỏng. Bạn nên chọn mặc định là Có khi được hỏi liệu bạn có muốn sử dụng Giao diện người dùng Trình mô phỏng hay không.

Khi kết thúc quá trình, bạn sẽ thấy kết quả đầu ra giống như ảnh chụp màn hình sau.

Quan trọng : Kết quả đầu ra của bạn có thể hơi khác so với của tôi, như được thấy trong ảnh chụp màn hình bên dưới, vì câu hỏi cuối cùng sẽ mặc định là "Không" nếu bạn đã tải xuống trình mô phỏng.

8544e41037637b07.png

Định cấu hình FlutterFire

Tiếp theo, bạn có thể sử dụng FlutterFire để tạo mã Dart cần thiết để sử dụng Firebase trong ứng dụng Flutter của mình.

flutterfire configure

Khi lệnh này chạy, bạn sẽ được nhắc chọn dự án Firebase nào bạn muốn sử dụng và nền tảng nào bạn muốn thiết lập. Trong lớp học lập trình này, các ví dụ sử dụng Flutter Web nhưng bạn có thể thiết lập dự án Firebase của mình để sử dụng tất cả các tùy chọn.

Các ảnh chụp màn hình sau đây hiển thị lời nhắc bạn cần trả lời.

619b7aca6dc15472.png301c9534f594f472.png

Ảnh chụp màn hình này hiển thị đầu ra ở cuối quá trình. Nếu đã quen thuộc với Firebase, bạn sẽ nhận thấy rằng bạn không cần phải tạo ứng dụng trong bảng điều khiển và FlutterFire CLI đã làm điều đó cho bạn.

12199a85ade30459.png

Thêm gói Firebase vào ứng dụng Flutter

Bước thiết lập cuối cùng là thêm các gói Firebase có liên quan vào dự án Flutter của bạn. Trong thiết bị đầu cuối, hãy đảm bảo bạn đang ở trong thư mục gốc của dự án Flutter tại flutter-codelabs/firebase-emulator-suite/start . Sau đó, chạy ba lệnh sau:

flutter pub add firebase_core
flutter pub add firebase_auth
flutter pub add cloud_firestore

Đây là những gói duy nhất bạn sẽ sử dụng trong ứng dụng này.

4. Kích hoạt trình giả lập Firebase

Cho đến nay, ứng dụng Flutter và dự án Firebase của bạn đã được thiết lập để có thể sử dụng trình mô phỏng, nhưng bạn vẫn cần yêu cầu mã Flutter định tuyến lại các yêu cầu Firebase gửi đi đến các cổng cục bộ.

Đầu tiên, thêm mã khởi tạo Firebase và mã thiết lập trình mô phỏng vào hàm main trong main.dart.

chính.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

import 'app_state.dart';
import 'firebase_options.dart';
import 'logged_in_view.dart';
import 'logged_out_view.dart';


void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Firebase.initializeApp(
   options: DefaultFirebaseOptions.currentPlatform,
 );

 if (kDebugMode) {
   try {
     FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080);
     await FirebaseAuth.instance.useAuthEmulator('localhost', 9099);
   } catch (e) {
     // ignore: avoid_print
     print(e);
   }
 }

 runApp(MyApp());
}

Một vài dòng mã đầu tiên khởi tạo Firebase. Hầu như trên toàn cầu, nếu bạn đang làm việc với Firebase trong ứng dụng Flutter, bạn muốn bắt đầu bằng cách gọi WidgetsFlutterBinding.ensureInitializedFirebase.initializeApp .

Sau đó, mã bắt đầu bằng dòng if (kDebugMode) sẽ yêu cầu ứng dụng của bạn nhắm mục tiêu trình mô phỏng thay vì dự án Firebase sản xuất. kDebugMode đảm bảo rằng việc nhắm mục tiêu trình mô phỏng sẽ chỉ xảy ra nếu bạn đang ở trong môi trường phát triển. Vì kDebugMode là một giá trị không đổi nên trình biên dịch Dart sẽ biết cách loại bỏ hoàn toàn khối mã đó trong chế độ phát hành.

Khởi động trình giả lập

Bạn nên khởi động trình mô phỏng trước khi khởi động ứng dụng Flutter. Đầu tiên, khởi động trình giả lập bằng cách chạy lệnh này trong terminal:

firebase emulators:start

Lệnh này khởi động trình mô phỏng và hiển thị các cổng localhost mà chúng ta có thể tương tác với chúng. Khi bạn chạy lệnh đó, bạn sẽ thấy kết quả tương tự như sau:

bb7181eb70829606.png

Kết quả đầu ra này cho bạn biết trình mô phỏng nào đang chạy và nơi bạn có thể đến để xem trình mô phỏng. Trước tiên, hãy kiểm tra giao diện người dùng trình mô phỏng tại localhost:4000 .

11563f4c7216de81.png

Đây là trang chủ cho giao diện người dùng của trình mô phỏng cục bộ. Nó liệt kê tất cả các trình giả lập có sẵn và mỗi trình mô phỏng được gắn nhãn trạng thái bật hoặc tắt.

5. Trình giả lập Firebase Auth

Trình mô phỏng đầu tiên bạn sẽ sử dụng là trình mô phỏng Xác thực. Bắt đầu với trình mô phỏng Auth bằng cách nhấp vào "Chuyển đến trình mô phỏng" trên thẻ Xác thực trong giao diện người dùng và bạn sẽ thấy một trang trông như thế này:

3c1bfded40733189.png

Trang này có điểm tương đồng với trang bảng điều khiển web Auth. Nó có một bảng liệt kê những người dùng như bảng điều khiển trực tuyến và cho phép bạn thêm người dùng theo cách thủ công. Một điểm khác biệt lớn ở đây là tùy chọn phương thức xác thực duy nhất có sẵn trên trình mô phỏng là qua Email và Mật khẩu. Điều này là đủ cho sự phát triển của địa phương.

Tiếp theo, bạn sẽ thực hiện quy trình thêm người dùng vào trình mô phỏng Firebase Auth, sau đó đăng nhập người dùng đó thông qua Flutter UI.

Thêm người dùng

Nhấp vào nút "Thêm người dùng" và điền thông tin sau vào biểu mẫu:

  • Tên hiển thị: Dấu gạch ngang
  • Email: dash@email.com
  • Mật khẩu: dashword

Gửi biểu mẫu và bạn sẽ thấy bảng hiện bao gồm một người dùng. Bây giờ bạn có thể cập nhật mã để đăng nhập với người dùng đó.

đã đăng nhập_out_view.dart

Mã duy nhất trong tiện ích LoggedOutView phải được cập nhật nằm trong lệnh gọi lại được kích hoạt khi người dùng nhấn nút đăng nhập. Cập nhật mã để trông như thế này:

class LoggedOutView extends StatelessWidget {
 final AppState state;
 const LoggedOutView({super.key, required this.state});
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: const Text('Firebase Emulator Suite Codelab'),
     ),
     body: Center(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: [
          Text(
           'Please log in',
            style: Theme.of(context).textTheme.displaySmall,
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: ElevatedButton(
             onPressed: () async {
              await state.logIn('dash@email.com', 'dashword').then((_) {
                if (state.user != null) {
                 context.go('/');
                }
              });
              },
              child: const Text('Log In'),
          ),
        ),
      ],
    ),
   ),
  );
 }
}

Mã cập nhật sẽ thay thế chuỗi TODO bằng email và mật khẩu bạn đã tạo trong trình mô phỏng xác thực. Và ở dòng tiếp theo, dòng if(true) đã được thay thế bằng mã kiểm tra xem state.user có rỗng hay không. Mã trong AppClass làm sáng tỏ hơn về điều này.

app_state.dart

Hai phần mã trong AppState cần được cập nhật. Đầu tiên, cung cấp cho thành viên lớp AppState.user loại User từ gói firebase_auth , thay vì loại Object .

Thứ hai, điền vào phương thức AppState.login như dưới đây:

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'entry.dart';

class AppState {
 AppState() {
   _entriesStreamController = StreamController.broadcast(onListen: () {
     _entriesStreamController.add([
       Entry(
         date: '10/09/2022',
         text: lorem,
         title: '[Example] My Journal Entry',
       )
     ]);
   });
 }

 User? user; // <-- changed variable type
 Stream<List<Entry>> get entries => _entriesStreamController.stream;
 late final StreamController<List<Entry>> _entriesStreamController;

 Future<void> logIn(String email, String password) async {
   final credential = await FirebaseAuth.instance
       .signInWithEmailAndPassword(email: email, password: password);
   if (credential.user != null) {
     user = credential.user!;
     _listenForEntries();
   } else {
     print('no user!');
   }
 } 
 // ...
}

Định nghĩa loại cho người dùng bây giờ là User? . Lớp User đó đến từ Firebase Auth và cung cấp thông tin cần thiết, chẳng hạn như User.displayName , sẽ được thảo luận sau.

Đây là mã cơ bản cần thiết để đăng nhập người dùng bằng email và mật khẩu trong Firebase Auth. Nó thực hiện lệnh gọi tới FirebaseAuth để đăng nhập, trả về đối tượng Future<UserCredential> . Khi tương lai hoàn tất, mã này sẽ kiểm tra xem có User nào được đính kèm với UserCredential hay không. Nếu có người dùng trên đối tượng thông tin xác thực thì người dùng đó đã đăng nhập thành công và thuộc tính AppState.user có thể được đặt. Nếu không có thì đã có lỗi và nó đã được in.

Lưu ý rằng dòng mã duy nhất trong phương thức này dành riêng cho ứng dụng này (chứ không phải mã FirebaseAuth chung) là lệnh gọi đến phương thức _listenForEntries , sẽ được đề cập trong bước tiếp theo.

VIỆC CẦN LÀM: Biểu tượng hành động – Tải lại ứng dụng của bạn rồi nhấn nút Đăng nhập khi ứng dụng hiển thị. Điều này khiến ứng dụng điều hướng đến trang có nội dung "Chào mừng trở lại, người!" ở trên cùng. Xác thực phải đang hoạt động vì nó cho phép bạn điều hướng đến trang này nhưng cần thực hiện một cập nhật nhỏ đối với logged_in_view.dart để hiển thị tên thực của người dùng.

đã đăng nhập_in_view.dart

Thay đổi dòng đầu tiên trong phương thức LoggedInView.build :

class LoggedInView extends StatelessWidget {
 final AppState state;
 LoggedInView({super.key, required this.state});

 final PageController _controller = PageController(initialPage: 1);

 @override
 Widget build(BuildContext context) {
   final name = state.user!.displayName ?? 'No Name';

   return Scaffold(
 // ...

Bây giờ, dòng này lấy displayName từ thuộc tính User trên đối tượng AppState . displayName này đã được đặt trong trình mô phỏng khi bạn xác định người dùng đầu tiên của mình. Ứng dụng của bạn bây giờ sẽ hiển thị "Chào mừng trở lại, Dash!" khi bạn đăng nhập, thay vì TODO .

6. Đọc và ghi dữ liệu vào trình giả lập Firestore

Đầu tiên, hãy kiểm tra trình giả lập Firestore. Trên trang chủ Giao diện người dùng Trình mô phỏng ( localhost:4000 ), nhấp vào "Chuyển đến trình mô phỏng" trên thẻ Firestore. Nó sẽ giống như thế này:

Giả lập:

791fce7dc137910a.png

Bảng điều khiển Firebase:

e0dde9aea34af050.png

Nếu bạn có bất kỳ trải nghiệm nào với Firestore, bạn sẽ nhận thấy rằng trang này trông giống với trang Firestore của bảng điều khiển Firebase. Tuy nhiên, có một số khác biệt đáng chú ý.

  1. Bạn có thể xóa tất cả dữ liệu chỉ bằng một lần nhấn nút. Điều này sẽ nguy hiểm với dữ liệu sản xuất nhưng rất hữu ích cho việc lặp lại nhanh chóng! Nếu bạn đang làm việc trên một dự án mới và mô hình dữ liệu của bạn thay đổi, bạn có thể dễ dàng giải quyết vấn đề này.
  2. Có một tab "Yêu cầu". Tab này cho phép bạn xem các yêu cầu gửi đến trình mô phỏng này. Tôi sẽ thảo luận chi tiết hơn về tab này một chút.
  3. Không có tab nào cho Quy tắc, Chỉ mục hoặc Cách sử dụng. Có một công cụ (được thảo luận ở phần tiếp theo) giúp viết các quy tắc bảo mật, nhưng bạn không thể đặt quy tắc bảo mật cho trình mô phỏng cục bộ.

Tóm lại danh sách đó, phiên bản Firestore này cung cấp nhiều công cụ hữu ích hơn trong quá trình phát triển và loại bỏ các công cụ cần thiết trong quá trình sản xuất.

Viết thư cho Firestore

Trước khi thảo luận về tab 'Yêu cầu' trong trình mô phỏng, trước tiên hãy đưa ra yêu cầu. Điều này yêu cầu cập nhật mã. Bắt đầu bằng cách kết nối biểu mẫu trong ứng dụng để viết nhật ký mới Entry vào Firestore.

Luồng cấp cao để gửi Entry là:

  1. Người dùng điền vào biểu mẫu và nhấn nút Submit
  2. Giao diện người dùng gọi AppState.writeEntryToFirebase
  3. AppState.writeEntryToFirebase thêm mục vào Firebase

Không có mã nào liên quan đến bước 1 hoặc 2 cần thay đổi. Mã duy nhất cần thêm cho bước 3 sẽ được thêm vào lớp AppState . Thực hiện thay đổi sau thành AppState.writeEntryToFirebase .

app_state.dart

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'entry.dart';

class AppState {
 AppState() {
   _entriesStreamController = StreamController.broadcast(onListen: () {
     _entriesStreamController.add([
       Entry(
         date: '10/09/2022',
         text: lorem,
         title: '[Example] My Journal Entry',
       )
     ]);
   });
 }

 User? user;
 Stream<List<Entry>> get entries => _entriesStreamController.stream;
 late final StreamController<List<Entry>> _entriesStreamController;

 Future<void> logIn(String email, String password) async {
   final credential = await FirebaseAuth.instance
       .signInWithEmailAndPassword(email: email, password: password);
   if (credential.user != null) {
     user = credential.user!;
     _listenForEntries();
   } else {
     print('no user!');
   }
 }

 void writeEntryToFirebase(Entry entry) {
   FirebaseFirestore.instance.collection('Entries').add(<String, String>{
     'title': entry.title,
     'date': entry.date.toString(),
     'text': entry.text,
   });
 }
 // ...
}

Mã trong phương thức writeEntryToFirebase lấy tham chiếu đến bộ sưu tập có tên "Entries" trong Firestore. Sau đó, nó thêm một mục mới, cần phải thuộc loại Map<String, String> .

Trong trường hợp này, bộ sưu tập "Entries" trong Firestore không tồn tại nên Firestore đã tạo một bộ sưu tập.

Với mã đó đã được thêm vào, hãy tải lại nóng hoặc khởi động lại ứng dụng của bạn, đăng nhập và điều hướng đến chế độ xem EntryForm . Bạn có thể điền vào biểu mẫu bất kỳ Strings nào bạn muốn. (Trường Ngày sẽ nhận bất kỳ Chuỗi nào vì trường này đã được đơn giản hóa cho lớp học lập trình này. Trường này không có tính xác thực mạnh mẽ hoặc không quan tâm đến các đối tượng DateTime theo bất kỳ cách nào.)

Nhấn gửi trên biểu mẫu. Sẽ không có gì xảy ra trong ứng dụng nhưng bạn có thể thấy mục nhập mới của mình trong giao diện người dùng trình mô phỏng.

Tab yêu cầu trong trình mô phỏng Firestore

Trong giao diện người dùng, điều hướng đến trình mô phỏng Firestore và xem tab "Dữ liệu". Bạn sẽ thấy rằng hiện tại có một Bộ sưu tập ở thư mục gốc cơ sở dữ liệu của bạn có tên là "Mục nhập". Điều đó phải có một tài liệu chứa thông tin giống như bạn đã nhập vào biểu mẫu.

a978fb34fb8a83da.png

Điều đó xác nhận rằng AppState.writeEntryToFirestore đã hoạt động và bây giờ bạn có thể khám phá thêm yêu cầu trong tab Yêu cầu. Hãy nhấp vào tab đó ngay bây giờ.

Yêu cầu trình mô phỏng Firestore

Tại đây, bạn sẽ thấy một danh sách trông giống như thế này:

f0b37f0341639035.png

Bạn có thể nhấp vào bất kỳ mục nào trong danh sách đó và xem khá nhiều thông tin hữu ích. Nhấp vào mục danh CREATE tương ứng với yêu cầu của bạn để tạo một mục nhật ký mới. Bạn sẽ thấy một bảng mới trông như thế này:

385d62152e99aad4.png

Như đã đề cập, trình mô phỏng Firestore cung cấp các công cụ để phát triển các quy tắc bảo mật cho ứng dụng của bạn. Chế độ xem này hiển thị chính xác dòng nào trong quy tắc bảo mật của bạn mà yêu cầu này đã được thông qua (hoặc không thành công, nếu trường hợp đó xảy ra). Trong một ứng dụng mạnh mẽ hơn, Quy tắc bảo mật có thể phát triển và có nhiều bước kiểm tra ủy quyền. Chế độ xem này được sử dụng để giúp viết và gỡ lỗi các quy tắc ủy quyền đó.

Nó cũng cung cấp một cách dễ dàng để kiểm tra từng phần của yêu cầu này, bao gồm siêu dữ liệu và dữ liệu xác thực. Dữ liệu này được sử dụng để viết các quy tắc ủy quyền phức tạp.

Đọc từ Firestore

Firestore sử dụng đồng bộ hóa dữ liệu để đẩy dữ liệu cập nhật đến các thiết bị được kết nối. Trong mã Flutter, bạn có thể nghe (hoặc đăng ký) các bộ sưu tập và tài liệu của Firestore và mã của bạn sẽ được thông báo bất kỳ khi nào dữ liệu thay đổi. Trong ứng dụng này, việc nghe các bản cập nhật của Firestore được thực hiện theo phương thức có tên AppState._listenForEntries .

Mã này hoạt động cùng với StreamControllerStream được gọi lần lượt là AppState._entriesStreamControllerAppState.entries . Mã đó đã được viết sẵn, cũng như tất cả mã cần thiết trong giao diện người dùng để hiển thị dữ liệu từ Firestore.

Cập nhật phương thức _listenForEntries để khớp với mã bên dưới:

app_state.dart

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'entry.dart';

class AppState {
 AppState() {
   _entriesStreamController = StreamController.broadcast(onListen: () {
     _entriesStreamController.add([
       Entry(
         date: '10/09/2022',
         text: lorem,
         title: '[Example] My Journal Entry',
       )
     ]);
   });
 }

 User? user;
 Stream<List<Entry>> get entries => _entriesStreamController.stream;
 late final StreamController<List<Entry>> _entriesStreamController;

 Future<void> logIn(String email, String password) async {
   final credential = await FirebaseAuth.instance
       .signInWithEmailAndPassword(email: email, password: password);
   if (credential.user != null) {
     user = credential.user!;
     _listenForEntries();
   } else {
     print('no user!');
   }
 }

 void writeEntryToFirebase(Entry entry) {
   FirebaseFirestore.instance.collection('Entries').add(<String, String>{
     'title': entry.title,
     'date': entry.date.toString(),
     'text': entry.text,
   });
 }

 void _listenForEntries() {
   FirebaseFirestore.instance
       .collection('Entries')
       .snapshots()
       .listen((event) {
     final entries = event.docs.map((doc) {
       final data = doc.data();
       return Entry(
         date: data['date'] as String,
         text: data['text'] as String,
         title: data['title'] as String,
       );
     }).toList();

     _entriesStreamController.add(entries);
   });
 }
 // ...
}

Mã này lắng nghe bộ sưu tập "Entries" trong Firestore. Khi Firestore thông báo cho ứng dụng khách này rằng có dữ liệu mới, nó sẽ chuyển dữ liệu đó và mã trong _listenForEntries sẽ thay đổi tất cả tài liệu con của nó thành một đối tượng mà ứng dụng của chúng tôi có thể sử dụng ( Entry ). Sau đó, nó thêm các mục đó vào StreamController có tên _entriesStreamController (UI đang nghe). Mã này là bản cập nhật duy nhất được yêu cầu.

Cuối cùng, hãy nhớ lại rằng phương thức AppState.logIn thực hiện lệnh gọi đến _listenForEntries , phương thức này sẽ bắt đầu quá trình nghe sau khi người dùng đăng nhập.

// ...
Future<void> logIn(String email, String password) async {
 final credential = await FirebaseAuth.instance
     .signInWithEmailAndPassword(email: email, password: password);
 if (credential.user != null) {
   user = credential.user!;
   _listenForEntries();
 } else {
   print('no user!');
 }
}
// ...

Bây giờ hãy chạy ứng dụng. Nó sẽ giống như thế này:

b8a31c7a8900331.gif

7. Xuất và nhập dữ liệu vào trình giả lập

Trình mô phỏng Firebase hỗ trợ nhập và xuất dữ liệu. Việc sử dụng tính năng nhập và xuất cho phép bạn tiếp tục phát triển với cùng một dữ liệu khi bạn tạm dừng phát triển và sau đó tiếp tục. Bạn cũng có thể chuyển các tệp dữ liệu sang git và các nhà phát triển khác mà bạn đang làm việc cùng sẽ có cùng dữ liệu để làm việc.

Xuất dữ liệu trình mô phỏng

Đầu tiên, xuất dữ liệu trình giả lập mà bạn đã có. Trong khi trình giả lập vẫn đang chạy, hãy mở một cửa sổ terminal mới và nhập lệnh sau:

firebase emulators:export ./emulators_data

.emulators_data là một đối số cho Firebase biết nơi xuất dữ liệu. Nếu thư mục không tồn tại, nó sẽ được tạo. Bạn có thể sử dụng bất kỳ tên nào bạn muốn cho thư mục đó.

Khi bạn chạy lệnh này, bạn sẽ thấy kết quả này trong terminal nơi bạn đã chạy lệnh:

i  Found running emulator hub for project flutter-firebase-codelab-d6b79 at http://localhost:4400
i  Creating export directory /Users/ewindmill/Repos/codelabs/firebase-emulator-suite/complete/emulators_data
i  Exporting data to: /Users/ewindmill/Repos/codelabs/firebase-emulator-suite/complete/emulators_data
✔  Export complete

Và nếu bạn chuyển sang cửa sổ terminal nơi trình giả lập đang chạy, bạn sẽ thấy kết quả đầu ra này:

i  emulators: Received export request. Exporting data to /Users/ewindmill/Repos/codelabs/firebase-emulator-suite/complete/emulators_data.
✔  emulators: Export complete.

Và cuối cùng, nếu nhìn vào thư mục dự án của mình, bạn sẽ thấy thư mục có tên ./emulators_data , chứa các tệp JSON , cùng với các tệp siêu dữ liệu khác, cùng với dữ liệu bạn đã lưu.

Nhập dữ liệu trình mô phỏng

Giờ đây, bạn có thể nhập dữ liệu đó như một phần của quy trình phát triển của mình và bắt đầu từ nơi bạn đã dừng lại.

Trước tiên, hãy dừng trình mô phỏng nếu chúng đang chạy bằng cách nhấn CTRL+C trong thiết bị đầu cuối của bạn.

Tiếp theo, hãy chạy lệnh emulators:start mà bạn đã thấy nhưng có gắn cờ cho biết dữ liệu nào cần nhập:

firebase emulators:start --import ./emulators_data

Khi trình mô phỏng hoạt động, hãy điều hướng đến giao diện người dùng trình mô phỏng tại localhost:4000 và bạn sẽ thấy cùng một dữ liệu mà bạn đã làm việc trước đó.

Tự động xuất dữ liệu khi đóng trình mô phỏng

Bạn cũng có thể tự động xuất dữ liệu khi thoát khỏi trình mô phỏng thay vì nhớ xuất dữ liệu vào cuối mỗi phiên phát triển.

Khi bạn khởi động trình mô phỏng, hãy chạy lệnh emulators:start với hai cờ bổ sung.

firebase emulators:start --import ./emulators_data --export-on-exit

Thì đấy! Dữ liệu của bạn bây giờ sẽ được lưu và tải lại mỗi khi bạn làm việc với trình mô phỏng cho dự án này. Bạn cũng có thể chỉ định một thư mục khác làm đối số cho –export-on-exit flag , nhưng nó sẽ mặc định là thư mục được chuyển đến –import .

Bạn cũng có thể sử dụng bất kỳ sự kết hợp nào của các tùy chọn này. Đây là ghi chú từ các tài liệu : Thư mục xuất có thể được chỉ định bằng cờ này: firebase emulators:start --export-on-exit=./saved-data . Nếu --import được sử dụng, đường dẫn xuất mặc định giống nhau; ví dụ: firebase emulators:start --import=./data-path --export-on-exit . Cuối cùng, nếu muốn, hãy chuyển các đường dẫn thư mục khác nhau tới các cờ --import--export-on-exit .

8. Xin chúc mừng!

Bạn đã hoàn thành Thiết lập và chạy với trình mô phỏng Firebase và Flutter. Bạn có thể tìm thấy mã hoàn chỉnh cho Codelab này trong thư mục "hoàn chỉnh" trên github: Flutter Codelabs

Những gì chúng tôi đã đề cập

  • Thiết lập ứng dụng Flutter để sử dụng Firebase
  • Thiết lập dự án Firebase
  • FlutterFire CLI
  • Firebase CLI
  • Trình mô phỏng xác thực Firebase
  • Trình mô phỏng Firebase Firestore
  • Nhập và xuất dữ liệu trình mô phỏng

Bước tiếp theo

Tìm hiểu thêm

Sparky tự hào về bạn!

2a0ad195769368b1.gif