Firebase for Flutter را بشناسید

1. قبل از شروع

در این کد لبه، برخی از اصول اولیه Firebase برای ایجاد برنامه های موبایل Flutter برای اندروید و iOS را خواهید آموخت.

پیش نیازها

این کد لبه فرض می کند که شما با Flutter آشنا هستید و Flutter SDK و یک ویرایشگر را نصب کرده اید.

آنچه شما ایجاد خواهید کرد

در این نرم‌افزار کد، یک رویداد RSVP و برنامه چت کتاب مهمان در Android، iOS، وب و macOS با استفاده از Flutter خواهید ساخت. کاربران را با Firebase Authentication احراز هویت می‌کنید و داده‌ها را با استفاده از Cloud Firestore همگام‌سازی می‌کنید.

آنچه شما نیاز دارید

شما می توانید این کد لبه را با استفاده از هر یک از دستگاه های زیر اجرا کنید:

  • یک دستگاه فیزیکی (اندروید یا iOS) به رایانه شما متصل شده و روی حالت توسعه دهنده تنظیم شده است.
  • شبیه ساز iOS (نیاز به نصب ابزار Xcode دارد.)
  • شبیه ساز اندروید (نیاز به راه اندازی در Android Studio دارد.)

علاوه بر موارد فوق، شما همچنین نیاز دارید:

  • مرورگر دلخواه شما، مانند کروم.
  • یک IDE یا ویرایشگر متن به انتخاب شما، مانند Android Studio یا VS Code که با پلاگین های Dart و Flutter پیکربندی شده است.
  • آخرین نسخه stable Flutter (یا beta اگر از زندگی در لبه لذت می برید).
  • یک حساب Google، مانند یک حساب جی میل، برای ایجاد و مدیریت پروژه Firebase شما.
  • ابزار خط فرمان firebase به حساب جیمیل شما وارد شده است.
  • کد نمونه آزمایشگاه کد. برای نحوه دریافت کد به مرحله بعدی مراجعه کنید.

2. کد نمونه را دریافت کنید

بیایید با دانلود نسخه اولیه پروژه خود از GitHub شروع کنیم.

مخزن GitHub را از خط فرمان کلون کنید:

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

یا اگر ابزار cli GitHub را نصب کرده اید:

gh repo clone flutter/codelabs flutter-codelabs

کد نمونه باید در دایرکتوری flutter-codelabs که حاوی کد مجموعه ای از codelabs است، کلون شود. کد این Codelab در flutter-codelabs/firebase-get-to-know-flutter است.

ساختار دایرکتوری تحت flutter-codelabs/firebase-get-to-know-flutter از عکس‌های فوری از جایی است که باید در انتهای هر مرحله نام‌گذاری شده باشید. این مرحله 2 است، بنابراین مکان یابی فایل های منطبق به آسانی:

cd flutter-codelabs/firebase-get-to-know-flutter/step_02

اگر می‌خواهید به جلو پرش کنید، یا ببینید چیزی بعد از یک مرحله چگونه باید باشد، به فهرستی که نام آن بر اساس مرحله مورد نظر شما است نگاه کنید.

برنامه شروع را وارد کنید

flutter-codelabs/firebase-get-to-know-flutter/step_02 را در IDE دلخواه خود باز کنید یا وارد کنید. این دایرکتوری حاوی کد شروع برای codelab است که از یک برنامه Flutter Meetup هنوز کاربردی نیست.

فایل های مورد نیاز برای کار را پیدا کنید

کد موجود در این برنامه در چندین دایرکتوری پخش شده است. این تقسیم عملکرد برای آسان‌تر کردن کار با گروه‌بندی کد بر اساس عملکرد طراحی شده است.

فایل های زیر را در پروژه پیدا کنید:

  • lib/main.dart : این فایل حاوی نقطه ورودی اصلی و ویجت برنامه است.
  • lib/src/widgets.dart : این فایل حاوی تعدادی ویجت است که به استاندارد کردن سبک برنامه کمک می کند. اینها برای نوشتن صفحه برنامه شروع کننده استفاده می شوند.
  • lib/src/authentication.dart : این فایل شامل اجرای جزئی FirebaseUI Auth با مجموعه ای از ویجت ها برای ایجاد یک تجربه کاربری برای ورود به سیستم برای احراز هویت مبتنی بر ایمیل Firebase است. این ویجت‌ها برای جریان احراز هویت هنوز در برنامه شروع استفاده نمی‌شوند، اما به زودی آنها را سیم‌کشی خواهید کرد.

شما فایل های اضافی را در صورت نیاز برای ساخت بقیه برنامه اضافه خواهید کرد.

بررسی فایل lib/main.dart

این برنامه از بسته google_fonts استفاده می کند تا ما را قادر سازد Roboto را به عنوان فونت پیش فرض در کل برنامه تبدیل کنیم. تمرینی برای خواننده با انگیزه این است که fonts.google.com را کاوش کند و از فونت هایی که در آنجا کشف می کنید در بخش های مختلف برنامه استفاده کند.

شما از ویجت های کمکی lib/src/widgets.dart به شکل Header ، Paragraph و IconAndDetail استفاده می کنید. این ویجت ها با حذف کدهای تکراری، درهم و HomePage را در طرح بندی صفحه که در صفحه اصلی توضیح داده شده است، کاهش می دهند. این مزیت اضافی را در ایجاد ظاهر و احساس ثابت دارد.

در اینجا برنامه شما در Android، iOS، وب و macOS به نظر می رسد:

پیش نمایش برنامه

3. یک پروژه Firebase ایجاد و راه اندازی کنید

نمایش اطلاعات رویداد برای مهمانان شما عالی است، اما فقط نشان دادن رویدادها برای هیچ کس چندان مفید نیست. بیایید برخی از عملکردهای پویا را به این برنامه اضافه کنیم. برای این کار، باید Firebase را به برنامه خود متصل کنید. برای شروع کار با Firebase، باید یک پروژه Firebase ایجاد و راه اندازی کنید.

یک پروژه Firebase ایجاد کنید

  1. وارد Firebase شوید.
  2. در کنسول Firebase، روی افزودن پروژه (یا ایجاد پروژه ) کلیک کنید و نام پروژه Firebase خود را Firebase-Flutter-Codelab بگذارید .

4395e4e67c08043a.png

  1. روی گزینه های ایجاد پروژه کلیک کنید. در صورت درخواست، شرایط Firebase را بپذیرید. از تنظیم Google Analytics صرفنظر کنید، زیرا از Analytics برای این برنامه استفاده نخواهید کرد.

b7138cde5f2c7b61.png

برای کسب اطلاعات بیشتر درباره پروژه‌های Firebase، به درک پروژه‌های Firebase مراجعه کنید.

برنامه‌ای که می‌سازید از چندین محصول Firebase استفاده می‌کند که برای برنامه‌های وب در دسترس هستند:

  • Firebase Authentication به کاربران شما اجازه می دهد تا به برنامه شما وارد شوند.
  • Cloud Firestore برای ذخیره داده های ساختاریافته در فضای ابری و دریافت اعلان فوری هنگام تغییر داده ها.
  • قوانین امنیتی Firebase برای ایمن سازی پایگاه داده شما.

برخی از این محصولات نیاز به پیکربندی خاصی دارند یا باید با استفاده از کنسول Firebase فعال شوند.

ورود به ایمیل را برای احراز هویت Firebase فعال کنید

برای اینکه به کاربران اجازه دهید وارد برنامه وب شوند، از روش ورود به سیستم ایمیل/گذرواژه برای این لبه کد استفاده خواهید کرد:

  1. در کنسول Firebase، منوی Build را در پانل سمت چپ گسترش دهید.
  2. روی تأیید هویت کلیک کنید، و سپس روی دکمه شروع ، سپس برگه روش ورود به سیستم (یا اینجا را کلیک کنید تا مستقیماً به برگه روش ورود به سیستم بروید).
  3. روی ایمیل/گذرواژه در لیست ارائه دهندگان ورود به سیستم کلیک کنید، سوئیچ Enable را روی موقعیت روشن قرار دهید و سپس روی ذخیره کلیک کنید. 58e3e3e23c2f16a4.png

Cloud Firestore را فعال کنید

برنامه وب از Cloud Firestore برای ذخیره پیام های چت و دریافت پیام های چت جدید استفاده می کند.

فعال کردن Cloud Firestore:

  1. در بخش ساخت کنسول Firebase، روی Cloud Firestore کلیک کنید.
  2. روی ایجاد پایگاه داده کلیک کنید. 99e8429832d23fa3.png
  1. گزینه Start in test mode را انتخاب کنید. سلب مسئولیت در مورد قوانین امنیتی را بخوانید. حالت تست تضمین می کند که می توانید آزادانه در حین توسعه در پایگاه داده بنویسید. روی Next کلیک کنید. 6be00e26c72ea032.png
  1. مکان پایگاه داده خود را انتخاب کنید (فقط می توانید از پیش فرض استفاده کنید). توجه داشته باشید که این مکان را نمی‌توان بعداً تغییر داد. 278656eefcfb0216.png
  2. روی Enable کلیک کنید.

4. پیکربندی Firebase

برای استفاده از Firebase با Flutter، باید فرآیندی را برای پیکربندی پروژه Flutter دنبال کنید تا از کتابخانه های FlutterFire به درستی استفاده شود:

  • وابستگی های FlutterFire را به پروژه خود اضافه کنید
  • پلتفرم مورد نظر را در پروژه Firebase ثبت کنید
  • فایل پیکربندی مخصوص پلتفرم را دانلود کرده و به کد اضافه کنید.

در دایرکتوری سطح بالای برنامه Flutter شما، زیرشاخه هایی به نام های android ، ios ، macos و web وجود دارد. این دایرکتوری ها به ترتیب فایل های پیکربندی مخصوص پلتفرم را برای iOS و اندروید نگهداری می کنند.

پیکربندی وابستگی ها

باید کتابخانه های FlutterFire را برای دو محصول Firebase که در این برنامه استفاده می کنید - Firebase Auth و Cloud Firestore اضافه کنید. سه دستور زیر را برای اضافه کردن وابستگی ها اجرا کنید.

$ flutter pub add firebase_core 
Resolving dependencies...
+ firebase_core 1.10.5
+ firebase_core_platform_interface 4.2.2
+ firebase_core_web 1.5.2
+ flutter_web_plugins 0.0.0 from sdk flutter
+ js 0.6.3
  test_api 0.4.3 (0.4.8 available)
Changed 5 dependencies!

firebase_core کد مشترک مورد نیاز برای همه پلاگین های Firebase Flutter است.

$ flutter pub add firebase_auth
Resolving dependencies...
+ firebase_auth 3.3.3
+ firebase_auth_platform_interface 6.1.8
+ firebase_auth_web 3.3.4
+ intl 0.17.0
  test_api 0.4.3 (0.4.8 available)
Changed 4 dependencies!

firebase_auth ادغام با قابلیت Authentication Firebase را امکان پذیر می کند.

$ flutter pub add cloud_firestore
Resolving dependencies...
+ cloud_firestore 3.1.4
+ cloud_firestore_platform_interface 5.4.9
+ cloud_firestore_web 2.6.4
  test_api 0.4.3 (0.4.8 available)
Changed 3 dependencies!

cloud_firestore دسترسی به فضای ذخیره سازی اطلاعات Cloud Firestore را امکان پذیر می کند.

$ flutter pub add provider
Resolving dependencies...
+ nested 1.0.0
+ provider 6.0.1
  test_api 0.4.3 (0.4.8 available)
Changed 2 dependencies!

در حالی که بسته‌های مورد نیاز را اضافه کرده‌اید، باید پروژه‌های iOS، Android، macOS و Web runner را نیز برای استفاده مناسب از Firebase پیکربندی کنید. شما همچنین از بسته provider استفاده می کنید که جداسازی منطق تجاری از منطق نمایش را امکان پذیر می کند.

نصب flutterfire

FlutterFire CLI به Firebase CLI زیرین بستگی دارد. اگر قبلاً این کار را انجام نداده اید، اطمینان حاصل کنید که Firebase CLI روی دستگاه شما نصب شده است.

سپس FlutterFire CLI را با اجرای دستور زیر نصب کنید:

$ dart pub global activate flutterfire_cli

پس از نصب، دستور flutterfire به صورت جهانی در دسترس خواهد بود.

در حال پیکربندی برنامه های خود

CLI اطلاعات پروژه Firebase و برنامه های پروژه انتخاب شده را استخراج می کند تا تمام تنظیمات را برای یک پلت فرم خاص ایجاد کند.

در ریشه برنامه خود، دستور configure را اجرا کنید:

$ flutterfire configure

دستور پیکربندی شما را از طریق تعدادی از فرآیندها راهنمایی می کند:

  1. انتخاب پروژه Firebase (بر اساس فایل .firebaserc یا از کنسول Firebase).
  2. درخواست کنید که برای چه پلتفرم هایی (مانند Android، iOS، macOS و وب) پیکربندی را می خواهید.
  3. مشخص کنید کدام برنامه های Firebase برای پلتفرم های انتخابی باید برای استخراج پیکربندی استفاده شوند. به طور پیش‌فرض، CLI سعی می‌کند به طور خودکار برنامه‌های Firebase را بر اساس پیکربندی پروژه فعلی شما مطابقت دهد.
  4. یک فایل firebase_options.dart در پروژه خود ایجاد کنید.

macOS را پیکربندی کنید

Flutter در macOS برنامه‌های کاملاً sandbox را می‌سازد. از آنجایی که این برنامه در حال ادغام با استفاده از شبکه برای برقراری ارتباط با سرورهای Firebase است، باید برنامه خود را با امتیازات کلاینت شبکه پیکربندی کنید.

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>

برای جزئیات بیشتر به حقوق و جعبه ایمنی برنامه مراجعه کنید.

5. افزودن ورود به سیستم کاربر (RSVP)

اکنون که Firebase را به برنامه اضافه کرده اید، می توانید یک دکمه RSVP راه اندازی کنید که افراد را با استفاده از Firebase Authentication ثبت می کند. برای اندروید بومی، iOS بومی و وب، بسته‌های FirebaseUI Auth از پیش ساخته شده‌اند، اما برای Flutter باید این قابلیت را ایجاد کنید.

پروژه ای که در مرحله 2 بازیابی کردید شامل مجموعه ای از ویجت ها بود که رابط کاربری را برای بیشتر جریان احراز هویت پیاده سازی می کند. شما منطق تجاری را برای ادغام احراز هویت Firebase در برنامه پیاده سازی خواهید کرد.

منطق کسب و کار با ارائه دهنده

شما می‌خواهید از بسته provider استفاده کنید تا یک شی حالت متمرکز برنامه را در سراسر درخت ابزارک‌های Flutter برنامه در دسترس قرار دهید. برای شروع، واردات را در بالای lib/main.dart :

lib/main.dart

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

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

خطوط import Firebase Core و Auth را معرفی می‌کنند، بسته provider را که برای در دسترس قرار دادن شی وضعیت برنامه از طریق درخت ویجت استفاده می‌کنید، وارد کرده و ویجت‌های احراز هویت از lib/src را شامل می‌شود.

این شیء حالت برنامه، ApplicationState ، دو مسئولیت اصلی برای این مرحله دارد، اما با افزودن قابلیت‌های بیشتر به برنامه در مراحل بعدی، مسئولیت‌های بیشتری نیز به دست خواهد آورد. اولین مسئولیت این است که کتابخانه Firebase را با یک فراخوانی به Firebase.initializeApp() مقداردهی کنید، و سپس مدیریت جریان مجوز وجود دارد. کلاس زیر را به انتهای lib/main.dart :

lib/main.dart

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

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

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
      } else {
        _loginState = ApplicationLoginState.loggedOut;
      }
      notifyListeners();
    });
  }

  ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
  ApplicationLoginState get loginState => _loginState;

  String? _email;
  String? get email => _email;

  void startLoginFlow() {
    _loginState = ApplicationLoginState.emailAddress;
    notifyListeners();
  }

  Future<void> verifyEmail(
    String email,
    void Function(FirebaseAuthException e) errorCallback,
  ) async {
    try {
      var methods =
          await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);
      if (methods.contains('password')) {
        _loginState = ApplicationLoginState.password;
      } else {
        _loginState = ApplicationLoginState.register;
      }
      _email = email;
      notifyListeners();
    } on FirebaseAuthException catch (e) {
      errorCallback(e);
    }
  }

  Future<void> signInWithEmailAndPassword(
    String email,
    String password,
    void Function(FirebaseAuthException e) errorCallback,
  ) async {
    try {
      await FirebaseAuth.instance.signInWithEmailAndPassword(
        email: email,
        password: password,
      );
    } on FirebaseAuthException catch (e) {
      errorCallback(e);
    }
  }

  void cancelRegistration() {
    _loginState = ApplicationLoginState.emailAddress;
    notifyListeners();
  }

  Future<void> registerAccount(
      String email,
      String displayName,
      String password,
      void Function(FirebaseAuthException e) errorCallback) async {
    try {
      var credential = await FirebaseAuth.instance
          .createUserWithEmailAndPassword(email: email, password: password);
      await credential.user!.updateDisplayName(displayName);
    } on FirebaseAuthException catch (e) {
      errorCallback(e);
    }
  }

  void signOut() {
    FirebaseAuth.instance.signOut();
  }
}

شایان ذکر است چند نکته کلیدی در این کلاس است. کاربر بدون احراز هویت شروع به کار می کند، برنامه فرمی را نشان می دهد که آدرس ایمیل کاربر را درخواست می کند، بسته به اینکه آیا آدرس ایمیل در پرونده است، برنامه یا از کاربر می خواهد ثبت نام کند یا رمز عبور او را درخواست می کند، و سپس با فرض اینکه همه چیز درست باشد، کاربر احراز هویت شده است.

لازم به ذکر است که این یک اجرای کامل جریان FirebaseUI Auth نیست، زیرا در مورد کاربری با یک حساب کاربری موجود که در ورود به سیستم مشکل دارد، رسیدگی نمی‌کند. پیاده‌سازی این قابلیت اضافی به عنوان تمرینی برای خواننده با انگیزه

یکپارچه سازی جریان احراز هویت

اکنون که حالت شروع برنامه را دارید، زمان آن است که وضعیت برنامه را به مقداردهی اولیه برنامه متصل کنید و جریان احراز هویت را به HomePage اصلی اضافه کنید. نقطه ورودی اصلی را برای ادغام وضعیت برنامه از طریق بسته provider به روز کنید:

lib/main.dart

void main() {
  // Modify from here
  runApp(
    ChangeNotifierProvider(
      create: (context) => ApplicationState(),
      builder: (context, _) => App(),
    ),
  );
  // to here.
}

تغییر در تابع main ، بسته ارائه‌دهنده را مسئول نمونه‌سازی شی حالت برنامه با استفاده از ویجت ChangeNotifierProvider . شما از این کلاس ارائه دهنده خاص استفاده می کنید زیرا شیء وضعیت برنامه ChangeNotifier را گسترش می دهد و این به بسته provider امکان می دهد بداند که چه زمانی ویجت های وابسته را دوباره نمایش دهد. در نهایت، با به‌روزرسانی روش build HomePage ، وضعیت برنامه را با Authentication ادغام کنید:

lib/main.dart

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

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

ویجت Authentication را نمونه‌سازی می‌کنید و آن را در یک ویجت Consumer قرار می‌دهید. ویجت Consumer روش معمولی است که بسته provider را می توان برای بازسازی بخشی از درخت هنگام تغییر وضعیت برنامه استفاده کرد. ویجت Authentication رابط کاربری احراز هویت است که اکنون آن را آزمایش خواهید کرد.

تست جریان احراز هویت

cdf2d25e436bd48d.png

در اینجا شروع جریان احراز هویت است، جایی که کاربر می تواند روی دکمه RSVP ضربه بزند تا فرم ایمیل را شروع کند.

2a2cd6d69d172369.png

با وارد کردن ایمیل، سیستم تأیید می کند که کاربر قبلاً ثبت نام کرده است، در این صورت از کاربر رمز عبور خواسته می شود، در غیر این صورت اگر کاربر ثبت نام نکرده باشد، از طریق فرم ثبت نام اقدام می کند.

e5e65065dba36b54.png

مطمئن شوید که سعی کنید یک رمز عبور کوتاه (کمتر از شش کاراکتر) وارد کنید تا جریان رسیدگی به خطا را بررسی کنید. اگر کاربر ثبت نام کرده باشد، در عوض رمز عبور را مشاهده خواهد کرد.

fbb3ea35fb4f67a.png

در این صفحه مطمئن شوید که رمزهای عبور نادرست را وارد کنید تا رسیدگی به خطا در این صفحه بررسی شود. در نهایت، پس از ورود کاربر، تجربه ورود به سیستم را مشاهده خواهید کرد که به کاربر امکان خروج مجدد را می دهد.

4ed811a25b0cf816.png

و با آن، شما یک جریان احراز هویت را پیاده سازی کرده اید. تبریک میگم

6. برای Cloud Firestore پیام بنویسید

دانستن اینکه کاربران در حال آمدن هستند عالی است، اما بیایید به مهمانان چیز دیگری در برنامه بدهیم تا انجام دهند. اگر آنها بتوانند در دفترچه مهمان پیام بگذارند چه؟ آنها می توانند به اشتراک بگذارند که چرا برای آمدن هیجان زده هستند یا با چه کسی امیدوارند ملاقات کنند.

برای ذخیره پیام‌های چتی که کاربران در برنامه می‌نویسند، از Cloud Firestore استفاده می‌کنید.

مدل داده

Cloud Firestore یک پایگاه داده NoSQL است و داده های ذخیره شده در پایگاه داده به مجموعه ها، اسناد، فیلدها و زیر مجموعه ها تقسیم می شود. شما هر پیام چت را به عنوان یک سند در یک مجموعه سطح بالا به نام guestbook ذخیره خواهید کرد.

7c20dc8424bb1d84.png

افزودن پیام به Firestore

در این بخش، قابلیت نوشتن پیام های جدید به پایگاه داده را برای کاربران اضافه می کنید. ابتدا عناصر رابط کاربری (فیلد فرم و دکمه ارسال) را اضافه می‌کنید و سپس کدی را اضافه می‌کنید که این عناصر را به پایگاه داده متصل می‌کند.

ابتدا، import برای بسته cloud_firestore و dart:async اضافه کنید.

lib/main.dart

import 'dart:async';                                    // new

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

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

برای ساختن عناصر رابط کاربری یک فیلد پیام و یک دکمه ارسال، یک ویجت حالت دار جدید GuestBook در پایین lib/main.dart کنید.

lib/main.dart

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

  @override
  _GuestBookState createState() => _GuestBookState();
}

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

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

در اینجا چند نکته جالب وجود دارد. در مرحله اول، شما در حال نمونه سازی یک فرم هستید تا بتوانید تأیید کنید که پیام واقعاً دارای محتوایی است و در صورت عدم وجود پیام خطایی به کاربر نشان دهید. راه اعتبارسنجی فرم شامل دسترسی به حالت فرم پشت فرم است و برای این کار از یک GlobalKey استفاده می کنید. برای اطلاعات بیشتر در مورد کلیدها و نحوه استفاده از آنها، لطفاً قسمت Flutter Widgets 101 "When to Use Keys" را ببینید.

همچنین به نحوه چیدمان ویجت ها توجه داشته باشید، شما یک Row با یک TextFormField و یک StyledButton که خود حاوی یک Row است. همچنین توجه داشته باشید که TextFormField در یک ویجت Expanded پیچیده شده است، این باعث می شود TextFormField هر فضای اضافی را در ردیف اشغال کند. برای درک بهتر اینکه چرا این مورد نیاز است، لطفاً درک محدودیت‌ها را مطالعه کنید.

اکنون که ویجتی دارید که به کاربر امکان می‌دهد متنی را برای افزودن به کتاب مهمان وارد کند، باید آن را روی صفحه نمایش دهید. برای انجام این کار، بدنه صفحه اصلی را ویرایش کنید تا دو خط زیر را در پایین HomePage فرزندان اضافه ListView :

const Header("What we'll be doing"),
const Paragraph(
  'Join us for a day full of Firebase Workshops and Pizza!',
),
// Add the following two lines.
const Header('Discussion'),
GuestBook(addMessage: (message) => print(message)),

در حالی که این برای نمایش ویجت کافی است، اما برای انجام کار مفید کافی نیست. شما به زودی این کد را به روز خواهید کرد تا کاربردی شود.

پیش نمایش برنامه

کاربری که روی دکمه SEND کلیک می کند، قطعه کد زیر را فعال می کند. محتویات فیلد ورودی پیام را به مجموعه guestbook پایگاه داده اضافه می کند. به طور خاص، روش addMessageToGuestBook محتوای پیام را به یک سند جدید (با شناسه ای که به طور خودکار تولید می شود) به مجموعه guestbook اضافه می کند.

توجه داشته باشید که FirebaseAuth.instance.currentUser.uid مرجعی است به شناسه منحصربه‌فرد تولید شده خودکار که Firebase Authentication برای همه کاربرانی که وارد سیستم شده‌اند ارائه می‌کند.

تغییر دیگری در فایل lib/main.dart ایجاد کنید. متد addMessageToGuestBook را اضافه کنید. در مرحله بعد رابط کاربری و این قابلیت را به هم متصل خواهید کرد.

lib/main.dart

class ApplicationState extends ChangeNotifier {

  // Current content of ApplicationState elided ...

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

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

سیم‌کشی UI به پایگاه داده

شما یک رابط کاربری دارید که در آن کاربر می‌تواند متنی را که می‌خواهد به کتاب مهمان اضافه کند، وارد کند، و شما کدی برای افزودن ورودی به Cloud Firestore دارید. اکنون تنها کاری که باید انجام دهید این است که این دو را به هم وصل کنید. در lib/main.dart تغییر زیر را در ویجت HomePage اعمال کنید.

lib/main.dart

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

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

شما دو خطی که در ابتدای این مرحله اضافه کردید را با اجرای کامل جایگزین کرده اید. شما مجدداً از Consumer<ApplicationState> استفاده می کنید تا وضعیت برنامه را برای بخشی از درختی که در حال ارائه آن هستید در دسترس قرار دهید. این به شما امکان می دهد به شخصی که پیامی را در UI وارد می کند واکنش نشان دهید و آن را در پایگاه داده منتشر کنید. در بخش بعدی بررسی خواهید کرد که آیا پیام های اضافه شده در پایگاه داده منتشر شده است یا خیر.

تست ارسال پیام

  1. مطمئن شوید که وارد برنامه شده اید.
  2. پیامی مانند "Hey There!" را وارد کنید و سپس روی SEND کلیک کنید.

این عمل پیام را در پایگاه داده Cloud Firestore شما می نویسد. با این حال، شما هنوز پیام را در برنامه Flutter واقعی خود نخواهید دید زیرا هنوز باید بازیابی داده ها را اجرا کنید. در مرحله بعد این کار را انجام خواهید داد.

اما می توانید پیام تازه اضافه شده را در کنسول Firebase ببینید.

در کنسول Firebase، در داشبورد پایگاه داده ، باید مجموعه guestbook را با پیامی که به تازگی اضافه کرده‌اید ببینید. اگر به ارسال پیام ادامه دهید، مجموعه کتاب مهمان شما حاوی اسناد زیادی است، مانند:

کنسول Firebase

713870af0b3b63c.png

7. پیام ها را بخوانید

دوست داشتنی است که مهمانان می توانند پیام هایی را در پایگاه داده بنویسند، اما هنوز نمی توانند آنها را در برنامه ببینند. بیایید درستش کنیم!

همگام سازی پیام ها

برای نمایش پیام‌ها، باید شنونده‌هایی اضافه کنید که هنگام تغییر داده‌ها فعال شوند و سپس یک عنصر رابط کاربری ایجاد کنید که پیام‌های جدید را نشان دهد. کدی را به حالت برنامه اضافه می‌کنید که به پیام‌های اضافه‌شده جدید از برنامه گوش می‌دهد.

درست بالای ویجت GuestBook کلاس مقدار زیر است. این کلاس یک نمای ساختاریافته از داده هایی که در Cloud Firestore ذخیره می کنید را نشان می دهد.

lib/main.dart

class GuestBookMessage {
  GuestBookMessage({required this.name, required this.message});
  final String name;
  final String message;
}

در قسمت ApplicationState که در آن حالت و دریافت کننده را تعریف می کنید، خطوط جدید زیر را اضافه کنید:

lib/main.dart

  ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
  ApplicationLoginState get loginState => _loginState;

  String? _email;
  String? get email => _email;

  // Add from here
  StreamSubscription<QuerySnapshot>? _guestBookSubscription;
  List<GuestBookMessage> _guestBookMessages = [];
  List<GuestBookMessage> get guestBookMessages => _guestBookMessages;
  // to here.

و در نهایت، در بخش مقداردهی اولیه ApplicationState ، موارد زیر را اضافه کنید تا در هنگام ورود کاربر به یک درخواست در مجموعه اسناد مشترک شوید و هنگامی که از سیستم خارج می شوید، اشتراک را لغو کنید.

lib/main.dart

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

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

این بخش مهم است، زیرا در اینجا جایی است که یک پرس و جو بر روی مجموعه guestbook ایجاد می کنید، و اشتراک و لغو اشتراک این مجموعه را انجام می دهید. شما به جریان گوش می دهید، جایی که یک حافظه پنهان محلی از پیام های مجموعه guestbook را بازسازی می کنید، و همچنین یک مرجع به این اشتراک ذخیره می کنید تا بتوانید بعداً اشتراک آن را لغو کنید. اینجا اتفاقات زیادی می افتد، و ارزش آن را دارد که مدتی را در یک دیباگر بگذرانیم تا ببینیم چه اتفاقی می افتد زمانی که یک مدل ذهنی واضح تر به دست آوریم.

برای اطلاعات بیشتر، به مستندات Cloud Firestore مراجعه کنید.

در ویجت GuestBook باید این حالت در حال تغییر را به رابط کاربری متصل کنید. شما ویجت را با افزودن لیستی از پیام ها به عنوان بخشی از پیکربندی آن تغییر می دهید.

lib/main.dart

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

  @override
  _GuestBookState createState() => _GuestBookState();
}

در مرحله بعد، این پیکربندی جدید را در _GuestBookState با تغییر روش build به صورت زیر نمایش می دهیم.

lib/main.dart

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

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

شما محتوای قبلی روش ساخت را با ویجت Column ، و سپس در انتهای فرزندان Column ، مجموعه‌ای برای ایجاد یک Paragraph جدید برای هر پیام در لیست پیام‌ها اضافه می‌کنید.

در نهایت، اکنون باید بدنه HomePage را به‌روزرسانی کنید تا GuestBook را با پارامتر messages جدید به درستی بسازید.

lib/main.dart

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

همگام سازی پیام ها را آزمایش کنید

Cloud Firestore به صورت خودکار و فوری داده ها را با مشتریان مشترک پایگاه داده همگام می کند.

  1. پیام هایی که قبلاً در پایگاه داده ایجاد کرده اید باید در برنامه نمایش داده شوند. با خیال راحت پیام های جدید بنویسید. آنها باید فورا ظاهر شوند.
  2. اگر فضای کاری خود را در چندین پنجره یا برگه باز کنید، پیام ها در زمان واقعی در بین برگه ها همگام می شوند.
  3. (اختیاری) می توانید مستقیماً در بخش پایگاه داده کنسول Firebase، پیام های جدید را حذف، اصلاح یا اضافه کنید. هر تغییری باید در UI ظاهر شود.

تبریک می گویم! شما در حال خواندن اسناد Cloud Firestore در برنامه خود هستید!

بررسی برنامه p

8. قوانین اساسی امنیتی را تنظیم کنید

شما در ابتدا Cloud Firestore را برای استفاده از حالت تست تنظیم کردید، به این معنی که پایگاه داده شما برای خواندن و نوشتن باز است. با این حال، شما فقط باید از حالت تست در مراحل اولیه توسعه استفاده کنید. به عنوان بهترین روش، باید قوانین امنیتی را برای پایگاه داده خود در حین توسعه برنامه خود تنظیم کنید. امنیت باید در ساختار و رفتار برنامه شما یکپارچه باشد.

قوانین امنیتی به شما امکان می دهد دسترسی به اسناد و مجموعه های موجود در پایگاه داده خود را کنترل کنید. سینتکس قوانین انعطاف پذیر به شما امکان می دهد قوانینی ایجاد کنید که هر چیزی را از همه نوشته ها گرفته تا کل پایگاه داده تا عملیات روی یک سند خاص مطابقت دهد.

می توانید قوانین امنیتی برای Cloud Firestore را در کنسول Firebase بنویسید:

  1. در بخش توسعه کنسول Firebase، روی Database کلیک کنید و سپس برگه Rules را انتخاب کنید (یا اینجا را کلیک کنید تا مستقیماً به برگه Rules بروید).
  2. باید قوانین امنیتی پیش فرض زیر را به همراه هشداری در مورد عمومی بودن قوانین مشاهده کنید.

7767a2d2e64e7275.png

مجموعه ها را شناسایی کنید

ابتدا مجموعه‌هایی را که برنامه داده‌ها را روی آنها می‌نویسد، شناسایی کنید.

در match /databases/{database}/documents , مجموعه ای را که می خواهید ایمن کنید شناسایی کنید:

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

قوانین امنیتی را اضافه کنید

از آنجایی که از UID احراز هویت به عنوان یک فیلد در هر سند دفترچه مهمان استفاده کرده‌اید، می‌توانید UID احراز هویت را دریافت کنید و تأیید کنید که هر کسی که قصد نوشتن در سند را دارد دارای یک UID تأیید هویت منطبق است.

مطابق شکل زیر قوانین خواندن و نوشتن را به مجموعه قوانین خود اضافه کنید:

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;
    }
  }
}

اکنون، برای کتاب مهمان، فقط کاربرانی که وارد سیستم شده‌اند می‌توانند پیام‌ها را بخوانند (هر پیامی!)، اما فقط نویسنده پیام می‌تواند یک پیام را ویرایش کند.

قوانین اعتبار سنجی را اضافه کنید

برای اطمینان از وجود تمام فیلدهای مورد انتظار در سند، تأیید اعتبار داده را اضافه کنید:

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. مرحله پاداش: آنچه را که آموخته اید تمرین کنید

وضعیت RSVP یک شرکت کننده را ثبت کنید

در حال حاضر، برنامه شما فقط به افراد این امکان را می دهد که در صورت علاقه مندی به رویداد، چت را شروع کنند. همچنین، تنها راهی که می‌توانید از آمدن کسی مطلع شوید این است که آن را در چت پست کند. بیایید سازماندهی کنیم و به مردم اطلاع دهیم که چند نفر می آیند.

شما قصد دارید چند قابلیت جدید را به حالت برنامه اضافه کنید. اولین مورد این است که کاربرانی که وارد سیستم شده اند، می توانند نامزدی را برای حضور یا عدم حضورشان اعلام کنند. قابلیت دوم شمارنده تعداد افرادی است که واقعاً در آن شرکت می کنند.

در lib/main.dart ، موارد زیر را به بخش Accessors اضافه کنید تا کد UI برای تعامل با این حالت فعال شود:

lib/main.dart

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

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

روش init ApplicationState را به صورت زیر به روز کنید:

lib/main.dart

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

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

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

مورد بالا یک پرس و جو همیشه مشترک را اضافه می کند تا تعداد شرکت کنندگان را بیابد، و یک پرس و جوی دوم را اضافه می کند که فقط زمانی فعال است که کاربر وارد سیستم شده است تا از حضور کاربر مطلع شود. بعد، شمارش زیر را بعد از اعلامیه GuestBookMessage اضافه کنید:

lib/main.dart

enum Attending { yes, no, unknown }

اکنون می خواهید ویجت جدیدی را تعریف کنید که مانند دکمه های رادیویی قدیمی عمل می کند. در حالت نامشخص شروع می شود، نه بله و نه خیر انتخاب شده است، اما زمانی که کاربر انتخاب کرد که آیا شرکت می کند یا نه، سپس آن گزینه را با یک دکمه پر شده نشان می دهید و گزینه دیگر را با یک رندر مسطح پس می گیرد.

lib/main.dart

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

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

در مرحله بعد، باید روش ساخت صفحه اصلی را به روز کنید تا از مزایای HomePage استفاده YesNoSelection ، و به کاربر وارد شده امکان می دهد در صورت حضور در آن نامزد شود. همچنین تعداد شرکت کنندگان در این رویداد را نمایش می دهید.

lib/main.dart

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

قوانین را اضافه کنید

از آنجایی که قبلاً قوانینی تنظیم کرده‌اید، داده‌های جدیدی که با دکمه‌ها اضافه می‌کنید رد می‌شوند. باید قوانین را به‌روزرسانی کنید تا اجازه دهید به مجموعه attendees اضافه شود.

برای مجموعه attendees ، از آنجایی که از Authentication UID به‌عنوان نام سند استفاده کرده‌اید، می‌توانید آن را بگیرید و بررسی کنید که uid همان سندی است که می‌نویسد. به همه اجازه می‌دهید فهرست شرکت‌کنندگان را بخوانند (زیرا هیچ داده خصوصی در آنجا وجود ندارد)، اما فقط سازنده باید بتواند آن را به‌روزرسانی کند.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // ... //
    match /attendees/{userId} {
      allow read: if true;
      allow write: if request.auth.uid == userId;
    }
  }
}

قوانین اعتبار سنجی را اضافه کنید

برای اطمینان از وجود تمام فیلدهای مورد انتظار در سند، تأیید اعتبار داده را اضافه کنید:

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;

    }
  }
}

(اختیاری) اکنون می توانید نتایج کلیک روی دکمه ها را مشاهده کنید. به داشبورد Cloud Firestore خود در کنسول Firebase بروید.

پیش نمایش برنامه

10. تبریک!

شما از Firebase برای ساختن یک برنامه وب تعاملی و بلادرنگ استفاده کرده اید!

آنچه را پوشش داده ایم

  • احراز هویت Firebase
  • Cloud Firestore
  • قوانین امنیتی Firebase

مراحل بعدی

  • آیا می خواهید درباره سایر محصولات Firebase اطلاعات بیشتری کسب کنید؟ شاید بخواهید فایل های تصویری را که کاربران آپلود می کنند ذخیره کنید؟ یا برای کاربران خود نوتیفیکیشن بفرستید؟ اسناد Firebase را بررسی کنید. آیا می خواهید درباره افزونه های Flutter برای Firebase بیشتر بدانید؟ برای اطلاعات بیشتر FlutterFire را بررسی کنید.
  • آیا می خواهید درباره Cloud Firestore بیشتر بدانید؟ شاید بخواهید در مورد زیر مجموعه ها و تراکنش ها بیاموزید؟ برای یافتن کدهایی که در Cloud Firestore عمیق‌تر می‌شود، به کدلابر وب Cloud Firestore بروید. یا برای آشنایی با Cloud Firestore این مجموعه YouTube را بررسی کنید !

بیشتر بدانید

چطور گذشت؟

بازخورد شما را دوست داریم! لطفاً یک فرم (بسیار) کوتاه را در اینجا پر کنید.