التطوير المحلي لتطبيقات Flutter الخاصة بك باستخدام Firebase Emulator Suite

1. قبل أن تبدأ

في هذا الدرس التطبيقي حول التعليمات البرمجية، ستتعلم كيفية استخدام Firebase Emulator Suite مع Flutter أثناء التطوير المحلي. ستتعلم كيفية استخدام مصادقة كلمة المرور للبريد الإلكتروني عبر Emulator Suite، وكيفية قراءة البيانات وكتابتها في محاكي Firestore. أخيرًا، ستعمل على استيراد وتصدير البيانات من المحاكيات، للعمل بنفس البيانات المزيفة في كل مرة تعود فيها إلى التطوير.

المتطلبات الأساسية

يفترض هذا الدرس التطبيقي حول البرمجة أن لديك بعض الخبرة في Flutter. إذا لم يكن الأمر كذلك، فقد ترغب في تعلم الأساسيات أولاً. الروابط التالية مفيدة:

يجب أن تتمتع أيضًا ببعض الخبرة في Firebase، لكن لا بأس إذا لم تقم مطلقًا بإضافة Firebase إلى مشروع Flutter. إذا لم تكن معتادًا على وحدة تحكم Firebase، أو كنت جديدًا تمامًا على Firebase تمامًا، فاطلع على الروابط التالية أولاً:

ما سوف تقوم بإنشائه

يرشدك هذا الدرس التطبيقي حول البرمجة إلى إنشاء تطبيق يوميات بسيط. سيحتوي التطبيق على شاشة تسجيل دخول، وشاشة تسمح لك بقراءة إدخالات دفتر اليومية السابقة وإنشاء إدخالات جديدة.

cd5c4753bbee8af.png8cb4d21f656540bf.png

ما ستتعلمه

ستتعلم كيفية البدء في استخدام Firebase، وكيفية دمج مجموعة Firebase Emulator واستخدامها في سير عمل تطوير Flutter. سيتم تناول موضوعات Firebase هذه:

لاحظ أنه يتم تناول هذه المواضيع بقدر ما تكون مطلوبة لتغطية مجموعة محاكي Firebase. يركز هذا الدرس التدريبي على البرمجة على إضافة مشروع Firebase إلى تطبيق Flutter، والتطوير باستخدام Firebase Emulator Suite. لن تكون هناك مناقشات متعمقة حول مصادقة Firebase أو Firestore. إذا لم تكن على دراية بهذه المواضيع، فنوصي بالبدء بالبرنامج التدريبي للتعرف على Firebase for Flutter .

ماذا ستحتاج

  • معرفة عملية بـ Flutter وتثبيت SDK
  • محررات النصوص Intellij JetBrains أو VS Code
  • متصفح Google Chrome (أو هدف التطوير المفضل لديك الآخر لـ Flutter. ستفترض بعض الأوامر الطرفية في هذا الدرس التطبيقي حول التعليمات البرمجية أنك تقوم بتشغيل تطبيقك على Chrome)

2. إنشاء وإعداد مشروع Firebase

المهمة الأولى التي ستحتاج إلى إكمالها هي إنشاء مشروع Firebase في وحدة تحكم الويب الخاصة بـ Firebase. ستركز الغالبية العظمى من دروس التعليمات البرمجية هذه على Emulator Suite، الذي يستخدم واجهة مستخدم تعمل محليًا، ولكن يتعين عليك إعداد مشروع Firebase كامل أولاً.

إنشاء مشروع Firebase

  1. قم بتسجيل الدخول إلى وحدة تحكم Firebase.
  2. في وحدة تحكم Firebase، انقر على "إضافة مشروع " (أو إنشاء مشروع )، وأدخل اسمًا لمشروع Firebase (على سبيل المثال، " Firebase-Flutter-Codelab") .

fe6aeab3b91965ed.png

  1. انقر فوق خيارات إنشاء المشروع. اقبل شروط Firebase إذا طُلب منك ذلك. تخطي إعداد Google Analytics، لأنك لن تستخدم Analytics لهذا التطبيق.

d1fcec48bf251eaa.png

لمعرفة المزيد حول مشاريع Firebase، راجع فهم مشاريع Firebase .

يستخدم التطبيق الذي تقوم بإنشائه اثنين من منتجات Firebase المتوفرة لتطبيقات Flutter:

  • مصادقة Firebase للسماح للمستخدمين بتسجيل الدخول إلى تطبيقك.
  • Cloud Firestore لحفظ البيانات المنظمة على السحابة وتلقي إشعار فوري عند تغيير البيانات.

يحتاج هذان المنتجان إلى تكوين خاص أو يجب تمكينهما باستخدام وحدة تحكم Firebase.

تمكين سحابة Firestore

يستخدم تطبيق Flutter Cloud Firestore لحفظ إدخالات دفتر اليومية.

تمكين Cloud Firestore:

  1. في قسم الإنشاء بوحدة تحكم Firebase، انقر فوق Cloud Firestore .
  2. انقر فوق إنشاء قاعدة بيانات . 99e8429832d23fa3.png
  3. حدد خيار البدء في وضع الاختبار . اقرأ إخلاء المسؤولية حول قواعد الأمان. يضمن وضع الاختبار إمكانية الكتابة بحرية إلى قاعدة البيانات أثناء التطوير. انقر فوق {التالي . 6be00e26c72ea032.png
  4. حدد موقع قاعدة البيانات الخاصة بك (يمكنك فقط استخدام الموقع الافتراضي). لاحظ أنه لا يمكن تغيير هذا الموقع لاحقًا. 278656eefcfb0216.png
  5. انقر فوق تمكين .

3. قم بإعداد تطبيق Flutter

ستحتاج إلى تنزيل رمز البدء وتثبيت Firebase CLI قبل أن نبدأ.

احصل على رمز البداية

انسخ مستودع GitHub من سطر الأوامر:

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

بدلاً من ذلك، إذا قمت بتثبيت أداة cli الخاصة بـ GitHub :

gh repo clone flutter/codelabs flutter-codelabs

يجب استنساخ نموذج التعليمات البرمجية في دليل flutter-codelabs ، الذي يحتوي على التعليمات البرمجية لمجموعة من Codelabs. الكود الخاص بهذا الدرس التطبيقي موجود في flutter-codelabs/firebase-emulator-suite .

بنية الدليل ضمن flutter-codelabs/firebase-emulator-suite عبارة عن مشروعين Flutter. أحدهما يُسمى complete ، والذي يمكنك الرجوع إليه إذا كنت تريد التخطي للأمام، أو إجراء إحالة مرجعية للتعليمات البرمجية الخاصة بك. المشروع الآخر يسمى start .

الكود الذي تريد البدء به موجود في الدليل flutter-codelabs/firebase-emulator-suite/start . افتح هذا الدليل أو قم باستيراده إلى IDE المفضل لديك.

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

قم بتثبيت Firebase CLI

يوفر Firebase CLI أدوات لإدارة مشاريع Firebase الخاصة بك. يلزم وجود واجهة سطر الأوامر (CLI) لاستخدام Emulator Suite، لذا ستحتاج إلى تثبيتها.

هناك مجموعة متنوعة من الطرق لتثبيت CLI. إن أبسط طريقة، إذا كنت تستخدم MacOS أو Linux، هي تشغيل هذا الأمر من جهازك الطرفي:

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

بعد تثبيت واجهة سطر الأوامر (CLI)، يجب عليك المصادقة باستخدام Firebase.

  1. قم بتسجيل الدخول إلى Firebase باستخدام حساب Google الخاص بك عن طريق تشغيل الأمر التالي:
firebase login
  1. يقوم هذا الأمر بتوصيل جهازك المحلي بـ Firebase ويمنحك حق الوصول إلى مشاريع Firebase الخاصة بك.
  1. اختبر ما إذا تم تثبيت واجهة سطر الأوامر (CLI) بشكل صحيح وإمكانية الوصول إلى حسابك من خلال إدراج مشاريع Firebase الخاصة بك. قم بتشغيل الأمر التالي:
firebase projects:list
  1. يجب أن تكون القائمة المعروضة مماثلة لمشاريع Firebase المدرجة في وحدة تحكم Firebase . يجب أن تشاهد على الأقل Firebase-flutter-codelab.

قم بتثبيت FlutterFire CLI

تم إنشاء FlutterFire CLI أعلى Firebase CLI، وهو يجعل دمج مشروع Firebase مع تطبيق Flutter أسهل.

أولاً، قم بتثبيت واجهة سطر الأوامر (CLI):

dart pub global activate flutterfire_cli

تأكد من تثبيت CLI. قم بتشغيل الأمر التالي داخل دليل مشروع Flutter وتأكد من قيام CLI بإخراج قائمة المساعدة.

flutterfire --help

استخدم Firebase CLI وFlutterFire CLI لإضافة مشروع Firebase إلى تطبيق Flutter

من خلال تثبيت اثنين من واجهات سطر الأوامر (CLI)، يمكنك إعداد منتجات Firebase فردية (مثل Firestore)، وتنزيل المحاكيات، وإضافة Firebase إلى تطبيق Flutter الخاص بك من خلال بضعة أوامر طرفية فقط.

أولاً، قم بإنهاء إعداد Firebase عن طريق تشغيل ما يلي:

firebase init

سيقودك هذا الأمر عبر سلسلة من الأسئلة اللازمة لإعداد مشروعك. تُظهر لقطات الشاشة هذه التدفق:

  1. عندما يُطلب منك تحديد الميزات، حدد "Firestore" و"المحاكيات". (لا يوجد خيار مصادقة، لأنه لا يستخدم التكوين الذي يمكن تعديله من ملفات مشروع Flutter الخاصة بك.) fe6401d769be8f53.png
  2. بعد ذلك، حدد "استخدام مشروع موجود" عندما يُطلب منك ذلك.

f11dcab439e6ac1e.png

  1. الآن، حدد المشروع الذي قمت بإنشائه في خطوة سابقة: Flutter-firebase-codelab.

3bdc0c6934991c25.png

  1. بعد ذلك، سيتم طرح سلسلة من الأسئلة عليك حول تسمية الملفات التي سيتم إنشاؤها. أقترح الضغط على "أدخل" لكل سؤال لتحديد الإعداد الافتراضي. 9bfa2d507e199c59.png
  2. وأخيرا، سوف تحتاج إلى تكوين المحاكيات. حدد Firestore and Authentication من القائمة، ثم اضغط على "Enter" لكل سؤال حول المنافذ المحددة التي سيتم استخدامها لكل محاكي. يجب عليك تحديد الإعداد الافتراضي، نعم، عند سؤالك عما إذا كنت تريد استخدام Emulator UI.

في نهاية العملية، يجب أن تشاهد نتيجة تبدو مثل لقطة الشاشة التالية.

هام : قد يكون ناتجك مختلفًا قليلاً عن مخرجي، كما هو موضح في لقطة الشاشة أدناه، لأن السؤال الأخير سيكون افتراضيًا على "لا" إذا كنت قد قمت بالفعل بتنزيل المحاكيات.

8544e41037637b07.png

تكوين FlutterFire

بعد ذلك، يمكنك استخدام FlutterFire لإنشاء رمز Dart المطلوب لاستخدام Firebase في تطبيق Flutter.

flutterfire configure

عند تشغيل هذا الأمر، سيُطلب منك تحديد مشروع Firebase الذي تريد استخدامه، والأنظمة الأساسية التي تريد إعدادها. في هذا الدرس التطبيقي حول التعليمات البرمجية، تستخدم الأمثلة Flutter Web، ولكن يمكنك إعداد مشروع Firebase الخاص بك لاستخدام جميع الخيارات.

تعرض لقطات الشاشة التالية المطالبات التي ستحتاج إلى الإجابة عليها.

619b7aca6dc15472.png301c9534f594f472.png

تعرض لقطة الشاشة هذه المخرجات في نهاية العملية. إذا كنت على دراية بـ Firebase، ستلاحظ أنه لم يكن عليك إنشاء تطبيقات في وحدة التحكم، وأن FlutterFire CLI قامت بذلك نيابةً عنك.

12199a85ade30459.png

أضف حزم Firebase إلى تطبيق Flutter

خطوة الإعداد النهائية هي إضافة حزم Firebase ذات الصلة إلى مشروع Flutter الخاص بك. في الوحدة الطرفية، تأكد من أنك في جذر مشروع Flutter على Flutter flutter-codelabs/firebase-emulator-suite/start . ثم قم بتشغيل الأوامر الثلاثة التالية:

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

هذه هي الحزم الوحيدة التي ستستخدمها في هذا التطبيق.

4. تمكين محاكيات Firebase

حتى الآن، تم إعداد تطبيق Flutter ومشروع Firebase الخاص بك لتتمكن من استخدام المحاكيات، ولكن لا تزال بحاجة إلى إخبار كود Flutter لإعادة توجيه طلبات Firebase الصادرة إلى المنافذ المحلية.

أولاً، أضف رمز تهيئة Firebase ورمز إعداد المحاكي إلى الوظيفة main في main.dart.

main.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());
}

تعمل الأسطر القليلة الأولى من التعليمات البرمجية على تهيئة Firebase. بشكل عام تقريبًا، إذا كنت تعمل مع Firebase في تطبيق Flutter، فأنت تريد البدء عن طريق الاتصال بـ WidgetsFlutterBinding.ensureInitialized و Firebase.initializeApp .

بعد ذلك، الكود الذي يبدأ بالسطر if (kDebugMode) يخبر تطبيقك باستهداف المحاكيات بدلاً من مشروع Firebase الإنتاجي. يضمن kDebugMode أن استهداف المحاكيات لن يحدث إلا إذا كنت في بيئة تطوير. نظرًا لأن kDebugMode هي قيمة ثابتة، يعرف مترجم Dart أنه يجب عليه إزالة كتلة التعليمات البرمجية هذه تمامًا في وضع الإصدار.

بدء تشغيل المحاكيات

يجب عليك تشغيل برامج المحاكاة قبل تشغيل تطبيق Flutter. أولاً، قم بتشغيل المحاكيات عن طريق تشغيل هذا في الوحدة الطرفية:

firebase emulators:start

يعمل هذا الأمر على تشغيل المحاكيات، ويكشف عن منافذ المضيف المحلي التي يمكننا التفاعل معها. عند تشغيل هذا الأمر، يجب أن ترى مخرجات مشابهة لما يلي:

bb7181eb70829606.png

يخبرك هذا الإخراج عن المحاكيات قيد التشغيل، وأين يمكنك الذهاب لرؤية المحاكيات. أولاً، تحقق من واجهة مستخدم المحاكي على localhost:4000 .

11563f4c7216de81.png

هذه هي الصفحة الرئيسية لواجهة مستخدم المحاكي المحلي. فهو يسرد جميع المحاكيات المتاحة، ويتم تصنيف كل واحدة منها بالحالة قيد التشغيل أو الإيقاف.

5. محاكي Firebase Auth

المحاكي الأول الذي ستستخدمه هو محاكي المصادقة. ابدأ باستخدام محاكي المصادقة بالنقر فوق "الانتقال إلى المحاكي" الموجود على بطاقة المصادقة في واجهة المستخدم، وسترى صفحة تبدو كالتالي:

3c1bfded40733189.png

تحتوي هذه الصفحة على أوجه تشابه مع صفحة وحدة تحكم ويب Auth. يحتوي على جدول يسرد المستخدمين مثل وحدة التحكم عبر الإنترنت، ويسمح لك بإضافة مستخدمين يدويًا. أحد الاختلافات الكبيرة هنا هو أن خيار طريقة المصادقة الوحيد المتاح على المحاكيات هو عبر البريد الإلكتروني وكلمة المرور. وهذا يكفي للتنمية المحلية.

بعد ذلك، ستتعرف على عملية إضافة مستخدم إلى محاكي Firebase Auth، ثم تسجيل دخول هذا المستخدم عبر Flutter UI.

إضافة مستخدم

انقر فوق الزر "إضافة مستخدم"، واملأ النموذج بهذه المعلومات:

  • اسم العرض: داش
  • البريد الإلكتروني: Dash@email.com
  • كلمة المرور: كلمة المرور

قم بإرسال النموذج، وسترى أن الجدول يتضمن الآن مستخدمًا. يمكنك الآن تحديث الرمز لتسجيل الدخول مع هذا المستخدم.

logged_out_view.dart

الكود الوحيد في عنصر واجهة مستخدم LoggedOutView الذي يجب تحديثه موجود في رد الاتصال الذي يتم تشغيله عندما يضغط المستخدم على زر تسجيل الدخول. قم بتحديث الكود ليبدو كالتالي:

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'),
          ),
        ),
      ],
    ),
   ),
  );
 }
}

يستبدل الكود المحدث سلاسل TODO بالبريد الإلكتروني وكلمة المرور اللذين قمت بإنشائهما في محاكي المصادقة. وفي السطر التالي، تم استبدال سطر if(true) بكود يتحقق مما إذا كان state.user خاليًا. يلقي الكود الموجود في AppClass مزيدًا من الضوء على هذا الأمر.

app_state.dart

يجب تحديث جزأين من التعليمات البرمجية في AppState . أولاً، امنح عضو الفصل AppState.user النوع User من حزمة firebase_auth ، بدلاً من النوع Object .

ثانيًا، قم بملء طريقة AppState.login كما هو موضح أدناه:

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!');
   }
 } 
 // ...
}

تعريف النوع للمستخدم هو الآن User? . تأتي فئة User هذه من Firebase Auth، وتوفر المعلومات المطلوبة مثل User.displayName ، والتي تمت مناقشتها قليلًا.

هذا هو الرمز الأساسي المطلوب لتسجيل دخول المستخدم باستخدام البريد الإلكتروني وكلمة المرور في Firebase Auth. فهو يقوم باستدعاء FirebaseAuth لتسجيل الدخول، مما يؤدي إلى إرجاع كائن Future<UserCredential> . عند اكتمال المستقبل، يتحقق هذا الرمز مما إذا كان هناك User مرتبط بـ UserCredential . إذا كان هناك مستخدم في كائن بيانات الاعتماد، فهذا يعني أن المستخدم قد قام بتسجيل الدخول بنجاح، ويمكن تعيين خاصية AppState.user . إذا لم يكن هناك، فهذا يعني أنه حدث خطأ، وتمت طباعته.

لاحظ أن السطر الوحيد من التعليمات البرمجية في هذه الطريقة الخاص بهذا التطبيق (بدلاً من كود FirebaseAuth العام) هو استدعاء الأسلوب _listenForEntries ، والذي سيتم تناوله في الخطوة التالية.

المهام: رمز الإجراء - أعد تحميل تطبيقك، ثم اضغط على زر تسجيل الدخول عندما يتم عرضه. يؤدي هذا إلى انتقال التطبيق إلى صفحة تقول "مرحبًا بعودتك أيها الشخص!" في القمة. يجب أن تكون المصادقة تعمل، لأنها تسمح لك بالانتقال إلى هذه الصفحة، ولكن يجب إجراء تحديث بسيط على logged_in_view.dart لعرض الاسم الفعلي للمستخدم.

logged_in_view.dart

قم بتغيير السطر الأول في طريقة 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(
 // ...

الآن، يلتقط هذا السطر displayName من خاصية User على كائن AppState . تم تعيين displayName هذا في المحاكي عندما قمت بتعريف المستخدم الأول الخاص بك. يجب أن يعرض تطبيقك الآن "مرحبًا بعودتك، داش!" عند تسجيل الدخول، بدلاً من TODO .

6. قراءة البيانات وكتابتها في محاكي Firestore

أولاً، تحقق من محاكي Firestore. في الصفحة الرئيسية لواجهة مستخدم Emulator ( localhost:4000 )، انقر فوق "الانتقال إلى المحاكي" على بطاقة Firestore. يجب أن تبدو هذه:

محاكي:

791fce7dc137910a.png

وحدة تحكم Firebase:

e0dde9aea34af050.png

إذا كانت لديك أي خبرة مع Firestore، ستلاحظ أن هذه الصفحة تبدو مشابهة لصفحة Firestore الخاصة بوحدة تحكم Firebase. هناك بعض الاختلافات الملحوظة، رغم ذلك.

  1. يمكنك مسح جميع البيانات بنقرة زر واحدة. قد يكون هذا أمرًا خطيرًا بالنسبة لبيانات الإنتاج، ولكنه مفيد للتكرار السريع! إذا كنت تعمل على مشروع جديد وتغير نموذج البيانات الخاص بك، فمن السهل مسحه.
  2. هناك علامة التبويب "الطلبات". تتيح لك علامة التبويب هذه مشاهدة الطلبات الواردة المقدمة إلى هذا المحاكي. سأناقش علامة التبويب هذه بمزيد من التفاصيل بعد قليل.
  3. لا توجد علامات تبويب للقواعد أو الفهارس أو الاستخدام. هناك أداة (تتم مناقشتها في القسم التالي) تساعد في كتابة قواعد الأمان، لكن لا يمكنك تعيين قواعد الأمان للمحاكي المحلي.

لتلخيص هذه القائمة، يوفر هذا الإصدار من Firestore المزيد من الأدوات المفيدة أثناء التطوير، ويزيل الأدوات المطلوبة في الإنتاج.

الكتابة إلى Firestore

قبل مناقشة علامة التبويب "الطلبات" في المحاكي، قم أولاً بتقديم طلب. وهذا يتطلب تحديثات التعليمات البرمجية. ابدأ بتوصيل النموذج الموجود في التطبيق لكتابة مجلة جديدة Entry to Firestore.

التدفق عالي المستوى لإرسال Entry هو:

  1. يقوم المستخدم بملء النموذج والضغط على زر Submit
  2. تستدعي واجهة المستخدم AppState.writeEntryToFirebase
  3. يضيف AppState.writeEntryToFirebase إدخالاً إلى Firebase

لا يلزم تغيير أي من التعليمات البرمجية الموجودة في الخطوة 1 أو 2. سيتم إضافة الكود الوحيد الذي يجب إضافته للخطوة 3 في فئة AppState . قم بإجراء التغيير التالي على 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,
   });
 }
 // ...
}

يلتقط الكود الموجود في طريقة writeEntryToFirebase مرجعًا إلى المجموعة المسماة "Entries" في Firestore. ثم يقوم بإضافة إدخال جديد، والذي يجب أن يكون من النوع Map<String, String> .

في هذه الحالة، لم تكن مجموعة "الإدخالات" في Firestore موجودة، لذلك أنشأ Firestore واحدة.

مع إضافة هذا الرمز، قم بإعادة تحميل تطبيقك أو إعادة تشغيله، وقم بتسجيل الدخول، وانتقل إلى طريقة العرض EntryForm . يمكنك ملء النموذج بأي Strings تريدها. (سيأخذ حقل "التاريخ" أي سلسلة، حيث تم تبسيطه في هذا الدرس التطبيقي حول التعليمات البرمجية. ولا يتمتع بتحقق قوي أو يهتم بكائنات DateTime بأي شكل من الأشكال.)

اضغط على إرسال في النموذج. لن يحدث أي شيء في التطبيق، ولكن يمكنك رؤية إدخالك الجديد في واجهة مستخدم المحاكي.

علامة تبويب الطلبات في محاكي Firestore

في واجهة المستخدم، انتقل إلى محاكي Firestore، وانظر إلى علامة التبويب "البيانات". يجب أن ترى أن هناك الآن مجموعة في جذر قاعدة البيانات الخاصة بك تسمى "الإدخالات". يجب أن يحتوي ذلك على مستند يحتوي على نفس المعلومات التي أدخلتها في النموذج.

a978fb34fb8a83da.png

يؤكد ذلك أن AppState.writeEntryToFirestore يعمل، ويمكنك الآن استكشاف الطلب بشكل أكبر في علامة التبويب "الطلبات". انقر فوق علامة التبويب هذه الآن.

طلبات محاكي Firestore

هنا، يجب أن تشاهد قائمة تبدو مشابهة لهذه:

f0b37f0341639035.png

يمكنك النقر فوق أي من عناصر القائمة هذه والاطلاع على قدر كبير من المعلومات المفيدة. انقر فوق عنصر القائمة CREATE الذي يتوافق مع طلبك لإنشاء إدخال دفتر يومية جديد. سيظهر لك جدول جديد يبدو كالتالي:

385d62152e99aad4.png

كما ذكرنا سابقًا، يوفر محاكي Firestore أدوات لتطوير قواعد أمان تطبيقك. يُظهر هذا العرض بالضبط السطر الموجود في قواعد الأمان لديك والذي مرره هذا الطلب (أو فشل، إذا كان الأمر كذلك). في تطبيق أكثر قوة، يمكن أن تنمو قواعد الأمان وتخضع لعمليات فحص ترخيص متعددة. يتم استخدام طريقة العرض هذه للمساعدة في كتابة قواعد التفويض وتصحيح الأخطاء فيها.

كما يوفر أيضًا طريقة سهلة لفحص كل جزء من هذا الطلب، بما في ذلك البيانات التعريفية وبيانات المصادقة. تُستخدم هذه البيانات لكتابة قواعد ترخيص معقدة.

القراءة من Firestore

يستخدم Firestore مزامنة البيانات لدفع البيانات المحدثة إلى الأجهزة المتصلة. في Flutter code، يمكنك الاستماع (أو الاشتراك) إلى مجموعات ومستندات Firestore، وسيتم إخطار الكود الخاص بك في أي وقت تتغير فيه البيانات. في هذا التطبيق، يتم الاستماع إلى تحديثات Firestore بالطريقة التي تسمى AppState._listenForEntries .

يعمل هذا الكود جنبًا إلى جنب مع StreamController و Stream المسمى AppState._entriesStreamController و AppState.entries ، على التوالي. هذا الرمز مكتوب بالفعل، كما هو الحال مع كل التعليمات البرمجية المطلوبة في واجهة المستخدم لعرض البيانات من Firestore.

قم بتحديث طريقة _listenForEntries لمطابقة الكود أدناه:

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);
   });
 }
 // ...
}

يستمع هذا الرمز إلى مجموعة "الإدخالات" في Firestore. عندما يقوم Firestore بإعلام هذا العميل بوجود بيانات جديدة، فإنه يمرر تلك البيانات ويقوم الكود الموجود في _listenForEntries بتغيير جميع المستندات الفرعية الخاصة به إلى كائن يمكن لتطبيقنا استخدامه ( Entry ). ثم يضيف هذه الإدخالات إلى StreamController المسمى _entriesStreamController (الذي تستمع إليه واجهة المستخدم). هذا الرمز هو التحديث الوحيد المطلوب.

أخيرًا، تذكر أن التابع AppState.logIn يقوم باستدعاء _listenForEntries ، والذي يبدأ عملية الاستماع بعد قيام المستخدم بتسجيل الدخول.

// ...
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!');
 }
}
// ...

الآن قم بتشغيل التطبيق. يجب أن تبدو هذه:

b8a31c7a8900331.gif

7. تصدير واستيراد البيانات إلى المحاكي

تدعم محاكيات Firebase استيراد البيانات وتصديرها. يتيح لك استخدام عمليات الاستيراد والتصدير مواصلة التطوير بنفس البيانات عندما تأخذ استراحة من التطوير ثم تستأنفه. يمكنك أيضًا إرسال ملفات البيانات إلى git، وسيكون لدى المطورين الآخرين الذين تعمل معهم نفس البيانات للعمل معها.

تصدير بيانات المحاكي

أولاً، قم بتصدير بيانات المحاكي الموجودة لديك بالفعل. بينما لا تزال المحاكيات قيد التشغيل، افتح نافذة طرفية جديدة، وأدخل الأمر التالي:

firebase emulators:export ./emulators_data

.emulators_data عبارة عن وسيطة تخبر Firebase بمكان تصدير البيانات. إذا كان الدليل غير موجود، فسيتم إنشاؤه. يمكنك استخدام أي اسم تريده لهذا الدليل.

عند تشغيل هذا الأمر، سترى هذا الإخراج في الوحدة الطرفية حيث قمت بتشغيل الأمر:

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

وإذا قمت بالتبديل إلى النافذة الطرفية التي تعمل فيها المحاكيات، فسترى هذا الإخراج:

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

وأخيرًا، إذا نظرت إلى دليل المشروع الخاص بك، فيجب أن ترى دليلًا يسمى ./emulators_data ، والذي يحتوي على ملفات JSON ، من بين ملفات البيانات الوصفية الأخرى، مع البيانات التي قمت بحفظها.

استيراد بيانات المحاكي

والآن، يمكنك استيراد تلك البيانات كجزء من سير عمل التطوير الخاص بك، والبدء من حيث توقفت.

أولاً، قم بإيقاف المحاكيات إذا كانت قيد التشغيل بالضغط على CTRL+C في جهازك الطرفي.

بعد ذلك، قم بتشغيل الأمر emulators:start الذي رأيته بالفعل، ولكن مع علامة تخبره بالبيانات التي سيتم استيرادها:

firebase emulators:start --import ./emulators_data

عند تشغيل المحاكيات، انتقل إلى واجهة مستخدم المحاكي على localhost:4000 ، وسترى نفس البيانات التي كنت تعمل معها سابقًا.

تصدير البيانات تلقائيًا عند إغلاق المحاكيات

يمكنك أيضًا تصدير البيانات تلقائيًا عند إنهاء المحاكيات، بدلاً من تذكر تصدير البيانات في نهاية كل جلسة تطوير.

عند بدء تشغيل المحاكيات، قم بتشغيل أمر emulators:start مع علامتين إضافيتين.

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

هاهو! سيتم الآن حفظ بياناتك وإعادة تحميلها في كل مرة تعمل فيها مع المحاكيات لهذا المشروع. يمكنك أيضًا تحديد دليل مختلف كوسيطة –export-on-exit flag ، ولكنه سيكون افتراضيًا للدليل الذي تم تمريره إلى –import .

يمكنك استخدام أي مجموعة من هذه الخيارات أيضًا. هذه هي الملاحظة من المستندات : يمكن تحديد دليل التصدير بهذه العلامة: firebase emulators:start --export-on-exit=./saved-data . إذا تم استخدام --import ، فإن مسار التصدير الافتراضي هو نفسه؛ على سبيل المثال: firebase emulators:start --import=./data-path --export-on-exit . وأخيرًا، إذا رغبت في ذلك، قم بتمرير مسارات دليل مختلفة إلى علامتي --import و --export-on-exit .

8. تهانينا!

لقد أكملت الإعداد والتشغيل باستخدام محاكي Firebase وFlutter. يمكنك العثور على الكود المكتمل لهذا Codelab في الدليل "الكامل" على github: Flutter Codelabs

ما قمنا بتغطيته

  • إعداد تطبيق Flutter لاستخدام Firebase
  • إعداد مشروع Firebase
  • فلاترفيري سطر الأوامر
  • Firebase CLI
  • محاكي مصادقة Firebase
  • محاكي Firebase Firestore
  • استيراد وتصدير بيانات المحاكي

الخطوات التالية

يتعلم أكثر

سباركي فخور بك!

2a0ad195769368b1.gif