Flutter के लिए Firebase के बारे में जानें

1. शुरू करने से पहले

इस कोडलैब में, Android और iOS के लिए Flutter मोबाइल ऐप्लिकेशन बनाने के लिए, Firebase की कुछ बुनियादी बातें बताई गई हैं.

ज़रूरी शर्तें

आपको क्या सीखने को मिलेगा

  • Flutter की मदद से, Android, iOS, वेब, और macOS पर इवेंट में शामिल होने के लिए न्योते का जवाब देने और मेहमानों की चैट बुक करने वाला ऐप्लिकेशन बनाने का तरीका.
  • Firebase Authentication की मदद से उपयोगकर्ताओं की पुष्टि कैसे करें और Firestore के साथ डेटा सिंक कैसे करें.

Android पर ऐप्लिकेशन की होम स्क्रीन

iOS पर ऐप्लिकेशन की होम स्क्रीन

आपको इनकी ज़रूरत होगी

इनमें से कोई भी डिवाइस:

  • आपके कंप्यूटर से कनेक्ट किया गया कोई Android या iOS डिवाइस, जिस पर डेवलपर मोड चालू हो.
  • iOS सिम्युलेटर (इसके लिए, Xcode टूल की ज़रूरत होती है).
  • Android एम्युलेटर (इसे Android Studio में सेट अप करना ज़रूरी है).

आपको इनकी भी ज़रूरत होगी:

  • अपनी पसंद का कोई ब्राउज़र, जैसे कि Google Chrome.
  • आपकी पसंद का कोई आईडीई या टेक्स्ट एडिटर, जिसमें Dart और Flutter प्लगिन कॉन्फ़िगर किए गए हों. जैसे, Android Studio या Visual Studio Code.
  • Flutter का नया stable वर्शन या beta वर्शन.
  • Firebase प्रोजेक्ट बनाने और उसे मैनेज करने के लिए, Google खाता.
  • Firebase CLI आपके Google खाते में लॉग इन हो.

2. सैंपल कोड पाना

GitHub से अपने प्रोजेक्ट का शुरुआती वर्शन डाउनलोड करें:

  1. कमांड लाइन से, flutter-codelabs डायरेक्ट्री में GitHub रिपॉज़िटरी का क्लोन बनाएं:
git clone https://github.com/flutter/codelabs.git flutter-codelabs

flutter-codelabs डायरेक्ट्री में, कोडलैब के कलेक्शन का कोड होता है. इस कोडलैब का कोड, flutter-codelabs/firebase-get-to-know-flutter डायरेक्ट्री में है. इस डायरेक्ट्री में स्नैपशॉट की एक सीरीज़ होती है. इससे पता चलता है कि हर चरण के आखिर में आपका प्रोजेक्ट कैसा दिखना चाहिए. उदाहरण के लिए, आप दूसरे चरण पर हैं.

  1. दूसरे चरण के लिए मिलती-जुलती फ़ाइलें ढूंढें:
cd flutter-codelabs/firebase-get-to-know-flutter/step_02

अगर आपको आगे बढ़ना है या यह देखना है कि किसी चरण के बाद कोई चीज़ कैसी दिखनी चाहिए, तो उस चरण के नाम वाली डायरेक्ट्री में देखें जिसमें आपकी दिलचस्पी है.

स्टार्टर ऐप्लिकेशन इंपोर्ट करना

  • अपनी पसंद के IDE में flutter-codelabs/firebase-get-to-know-flutter/step_02 डायरेक्ट्री खोलें या इंपोर्ट करें. इस डायरेक्ट्री में कोडलैब के लिए स्टार्टर कोड है. इसमें फ़िलहाल काम न करने वाला Flutter मीटअप ऐप्लिकेशन शामिल है.

उन फ़ाइलों को ढूंढें जिन पर काम करना है

इस ऐप्लिकेशन का कोड, कई डायरेक्ट्री में फैला हुआ है. फ़ंक्शन को इस तरह से बांटने से काम आसान हो जाता है, क्योंकि इससे कोड को फ़ंक्शन के हिसाब से ग्रुप किया जाता है.

  • इन फ़ाइलों का पता लगाएं:
    • lib/main.dart: इस फ़ाइल में मुख्य एंट्री पॉइंट और ऐप्लिकेशन विजेट शामिल होता है.
    • lib/home_page.dart: इस फ़ाइल में होम पेज का विजेट होता है.
    • lib/src/widgets.dart: इस फ़ाइल में कुछ विजेट शामिल हैं. इनकी मदद से, ऐप्लिकेशन की स्टाइल को स्टैंडर्ड बनाया जा सकता है. ये विजेट, स्टार्टर ऐप्लिकेशन की स्क्रीन बनाते हैं.
    • lib/src/authentication.dart: इस फ़ाइल में, Authentication को आंशिक तौर पर लागू किया गया है. इसमें विजेट का एक सेट भी शामिल है. इसकी मदद से, Firebase में ईमेल के ज़रिए पुष्टि करने की सुविधा के लिए, लॉगिन करने वाले उपयोगकर्ता का अनुभव बेहतर बनाया जा सकता है. फ़िलहाल, पुष्टि करने के फ़्लो के लिए इन विजेट का इस्तेमाल स्टार्टर ऐप्लिकेशन में नहीं किया गया है. हालांकि, इन्हें जल्द ही जोड़ा जाएगा.

ऐप्लिकेशन के बाकी हिस्से को बनाने के लिए, ज़रूरत के मुताबिक अन्य फ़ाइलें जोड़ी जाती हैं.

lib/main.dart फ़ाइल की समीक्षा करें

यह ऐप्लिकेशन, google_fonts पैकेज का इस्तेमाल करता है, ताकि पूरे ऐप्लिकेशन में Roboto को डिफ़ॉल्ट फ़ॉन्ट के तौर पर इस्तेमाल किया जा सके. fonts.google.com पर जाकर, अलग-अलग फ़ॉन्ट देखे जा सकते हैं. साथ ही, ऐप्लिकेशन के अलग-अलग हिस्सों में इन फ़ॉन्ट का इस्तेमाल किया जा सकता है.

आपने lib/src/widgets.dart फ़ाइल से हेल्पर विजेट का इस्तेमाल किया हो. ये विजेट Header, Paragraph, और IconAndDetail के तौर पर उपलब्ध होते हैं. इन विजेट की मदद से, डुप्लीकेट कोड को हटाया जा सकता है. इससे HomePage में बताए गए पेज लेआउट में मौजूद गड़बड़ी को कम किया जा सकता है. इससे, एक जैसा लुक और फ़ील भी मिलता है.

Android, iOS, वेब, और macOS पर आपका ऐप्लिकेशन ऐसा दिखता है:

Android पर ऐप्लिकेशन की होम स्क्रीन

iOS पर ऐप्लिकेशन की होम स्क्रीन

वेब पर ऐप्लिकेशन की होम स्क्रीन

macOS पर ऐप्लिकेशन की होम स्क्रीन

3. Firebase प्रोजेक्ट बनाना और उसे सेट अप करना

मेहमानों के लिए इवेंट की जानकारी दिखाना बहुत अच्छा है, लेकिन यह जानकारी अपने-आप में किसी के लिए भी बहुत काम की नहीं होती. आपको ऐप्लिकेशन में कुछ डाइनैमिक फ़ंक्शन जोड़ने होंगे. इसके लिए, आपको Firebase को अपने ऐप्लिकेशन से कनेक्ट करना होगा. Firebase का इस्तेमाल शुरू करने के लिए, आपको एक Firebase प्रोजेक्ट बनाना और उसे सेट अप करना होगा.

Firebase प्रोजेक्ट बनाना

  1. अपने Google खाते का इस्तेमाल करके, Firebase कंसोल में साइन इन करें.
  2. नया प्रोजेक्ट बनाने के लिए, बटन पर क्लिक करें. इसके बाद, प्रोजेक्ट का नाम डालें. उदाहरण के लिए, Firebase-Flutter-Codelab.
  3. जारी रखें पर क्लिक करें.
  4. अगर आपसे कहा जाए, तो Firebase की शर्तें पढ़ें और स्वीकार करें. इसके बाद, जारी रखें पर क्लिक करें.
  5. (ज़रूरी नहीं) Firebase कंसोल में एआई की मदद पाने की सुविधा चालू करें. इसे "Firebase में Gemini" कहा जाता है.
  6. इस कोडलैब के लिए, आपको Google Analytics की ज़रूरत नहीं है. इसलिए, Google Analytics के विकल्प को टॉगल करके बंद करें.
  7. प्रोजेक्ट बनाएं पर क्लिक करें. इसके बाद, प्रोजेक्ट के प्रोविज़न होने का इंतज़ार करें. इसके बाद, जारी रखें पर क्लिक करें.

Firebase प्रोजेक्ट के बारे में ज़्यादा जानने के लिए, Firebase प्रोजेक्ट के बारे में जानकारी लेख पढ़ें.

Firebase प्रॉडक्ट सेट अप करना

ऐप्लिकेशन, Firebase के इन प्रॉडक्ट का इस्तेमाल करता है. ये वेब ऐप्लिकेशन के लिए उपलब्ध हैं:

  • पुष्टि करना: इससे उपयोगकर्ताओं को आपके ऐप्लिकेशन में साइन इन करने की सुविधा मिलती है.
  • Firestore: यह क्लाउड पर स्ट्रक्चर्ड डेटा सेव करता है. साथ ही, डेटा में बदलाव होने पर तुरंत सूचनाएं भेजता है.
  • Firebase के सुरक्षा नियम: ये आपके डेटाबेस को सुरक्षित रखते हैं.

इनमें से कुछ प्रॉडक्ट के लिए, खास कॉन्फ़िगरेशन की ज़रूरत होती है. इसके अलावा, आपको इन्हें Firebase कंसोल में चालू करना होगा.

ईमेल पते से साइन इन करने की पुष्टि करने की सुविधा चालू करना

  1. Firebase कंसोल के प्रोजेक्ट की खास जानकारी वाले पैनल में, बनाएं मेन्यू को बड़ा करें.
  2. पुष्टि करें > शुरू करें > साइन-इन करने का तरीका > ईमेल/पासवर्ड > चालू करें > सेव करें पर क्लिक करें.

58e3e3e23c2f16a4.png

Firestore सेट अप करना

यह वेब ऐप्लिकेशन, चैट मैसेज सेव करने और नए चैट मैसेज पाने के लिए Firestore का इस्तेमाल करता है.

अपने Firebase प्रोजेक्ट में Firestore को सेट अप करने का तरीका यहां बताया गया है:

  1. Firebase कंसोल के बाईं ओर मौजूद पैनल में, बनाएं को बड़ा करें. इसके बाद, Firestore डेटाबेस को चुनें.
  2. डेटाबेस बनाएं पर क्लिक करें.
  3. डेटाबेस आईडी को (default) पर सेट रहने दें.
  4. अपने डेटाबेस के लिए कोई जगह चुनें. इसके बाद, आगे बढ़ें पर क्लिक करें.
    किसी असली ऐप्लिकेशन के लिए, आपको ऐसी जगह चुननी चाहिए जो आपके उपयोगकर्ताओं के आस-पास हो.
  5. टेस्ट मोड में शुरू करें पर क्लिक करें. सुरक्षा नियमों के बारे में डिसक्लेमर पढ़ें.
    इस कोडलैब में बाद में, अपने डेटा को सुरक्षित करने के लिए सुरक्षा नियम जोड़े जाएंगे. अपने डेटाबेस के लिए सुरक्षा के नियम जोड़े बिना, सार्वजनिक तौर पर किसी ऐप्लिकेशन को डिस्ट्रिब्यूट या उपलब्ध न करें.
  6. बनाएं पर क्लिक करें.

4. Firebase को कॉन्फ़िगर करना

Flutter के साथ Firebase का इस्तेमाल करने के लिए, आपको ये टास्क पूरे करने होंगे. इससे FlutterFire लाइब्रेरी का सही तरीके से इस्तेमाल करने के लिए, Flutter प्रोजेक्ट को कॉन्फ़िगर किया जा सकेगा:

  1. अपने प्रोजेक्ट में FlutterFire डिपेंडेंसी जोड़ें.
  2. Firebase प्रोजेक्ट पर, अपने हिसाब से प्लैटफ़ॉर्म रजिस्टर करें.
  3. प्लेटफ़ॉर्म के हिसाब से कॉन्फ़िगरेशन फ़ाइल डाउनलोड करें. इसके बाद, उसे कोड में जोड़ें.

आपके Flutter ऐप्लिकेशन की टॉप-लेवल डायरेक्ट्री में android, ios, macos, और web सबडायरेक्ट्री होती हैं. इनमें iOS और Android के लिए, प्लैटफ़ॉर्म के हिसाब से कॉन्फ़िगरेशन फ़ाइलें होती हैं.

डिपेंडेंसी कॉन्फ़िगर करना

आपको इस ऐप्लिकेशन में इस्तेमाल किए जाने वाले Firebase के दो प्रॉडक्ट के लिए, FlutterFire लाइब्रेरी जोड़नी होंगी: Authentication और Firestore.

  • कमांड लाइन से, ये डिपेंडेंसी जोड़ें:
$ flutter pub add firebase_core

firebase_core package, सभी Firebase Flutter प्लगिन के लिए ज़रूरी कॉमन कोड है.

$ flutter pub add firebase_auth

firebase_auth पैकेज की मदद से, पुष्टि करने की सुविधा को इंटिग्रेट किया जा सकता है.

$ flutter pub add cloud_firestore

cloud_firestore पैकेज की मदद से, Firestore के डेटा स्टोरेज को ऐक्सेस किया जा सकता है.

$ flutter pub add provider

firebase_ui_auth पैकेज, विजेट और यूटिलिटी का एक सेट उपलब्ध कराता है. इससे डेवलपर को पुष्टि करने के फ़्लो को तेज़ी से पूरा करने में मदद मिलती है.

$ flutter pub add firebase_ui_auth

आपने ज़रूरी पैकेज जोड़ लिए हैं. हालांकि, Firebase का सही तरीके से इस्तेमाल करने के लिए, आपको iOS, Android, macOS, और वेब रनर प्रोजेक्ट को भी कॉन्फ़िगर करना होगा. provider पैकेज का इस्तेमाल किया जाता है. इससे, कारोबार के लॉजिक को डिसप्ले लॉजिक से अलग किया जा सकता है.

FlutterFire सीएलआई इंस्टॉल करना

FlutterFire CLI, Firebase CLI पर निर्भर करता है.

  1. अगर आपने अब तक ऐसा नहीं किया है, तो अपने कंप्यूटर पर Firebase CLI इंस्टॉल करें.
  2. FlutterFire CLI इंस्टॉल करें:
$ dart pub global activate flutterfire_cli

इंस्टॉल हो जाने के बाद, flutterfire कमांड दुनिया भर में उपलब्ध होती है.

अपने ऐप्लिकेशन कॉन्फ़िगर करना

सीएलआई, आपके Firebase प्रोजेक्ट और चुने गए प्रोजेक्ट ऐप्लिकेशन से जानकारी निकालता है. इससे किसी प्लैटफ़ॉर्म के लिए पूरा कॉन्फ़िगरेशन जनरेट किया जा सकता है.

अपने ऐप्लिकेशन के रूट में, configure कमांड चलाएं:

$ flutterfire configure

कॉन्फ़िगरेशन कमांड की मदद से, ये काम किए जा सकते हैं:

  1. .firebaserc फ़ाइल के आधार पर या Firebase कंसोल से कोई Firebase प्रोजेक्ट चुनें.
  2. कॉन्फ़िगरेशन के लिए प्लैटफ़ॉर्म तय करें. जैसे, Android, iOS, macOS, और वेब.
  3. उन Firebase ऐप्लिकेशन की पहचान करें जिनसे कॉन्फ़िगरेशन निकालना है. डिफ़ॉल्ट रूप से, सीएलआई आपके मौजूदा प्रोजेक्ट कॉन्फ़िगरेशन के आधार पर, Firebase ऐप्लिकेशन को अपने-आप मैच करने की कोशिश करता है.
  4. अपने प्रोजेक्ट में firebase_options.dart फ़ाइल जनरेट करें.

macOS को कॉन्फ़िगर करना

macOS पर Flutter, पूरी तरह से सैंडबॉक्स किए गए ऐप्लिकेशन बनाता है. यह ऐप्लिकेशन, 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>

ज़्यादा जानकारी के लिए, डेस्कटॉप पर Flutter का इस्तेमाल करना लेख पढ़ें.

5. जवाब देने की सुविधा जोड़ना

ऐप्लिकेशन में Firebase जोड़ने के बाद, RSVP बटन बनाया जा सकता है. इससे उन लोगों को रजिस्टर किया जा सकेगा जिन्होंने Authentication का इस्तेमाल किया है. Android नेटिव, iOS नेटिव, और वेब के लिए, पहले से बने हुए FirebaseUI Auth पैकेज उपलब्ध हैं. हालांकि, आपको Flutter के लिए यह सुविधा बनानी होगी.

आपने जिस प्रोजेक्ट को पहले वापस पाया था उसमें विजेट का एक सेट शामिल था. यह सेट, पुष्टि करने के ज़्यादातर फ़्लो के लिए उपयोगकर्ता इंटरफ़ेस लागू करता है. ऐप्लिकेशन के साथ पुष्टि करने की सुविधा को इंटिग्रेट करने के लिए, कारोबार के लॉजिक को लागू करें.

Provider पैकेज की मदद से, कारोबार से जुड़ी लॉजिक लेयर जोड़ना

provider पैकेज का इस्तेमाल करके, ऐप्लिकेशन के पूरे फ़्लटर विजेट ट्री में ऐप्लिकेशन स्टेट ऑब्जेक्ट उपलब्ध कराएं:

  1. app_state.dart नाम की एक नई फ़ाइल बनाएं और उसमें यह कॉन्टेंट शामिल करें:

lib/app_state.dart

import 'package:firebase_auth/firebase_auth.dart'
    hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'firebase_options.dart';

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

  bool _loggedIn = false;
  bool get loggedIn => _loggedIn;

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

    FirebaseUIAuth.configureProviders([
      EmailAuthProvider(),
    ]);

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loggedIn = true;
      } else {
        _loggedIn = false;
      }
      notifyListeners();
    });
  }
}

import स्टेटमेंट, Firebase Core और Auth को पेश करते हैं. साथ ही, provider पैकेज को पुल करते हैं, जो पूरे विजेट ट्री में ऐप्लिकेशन स्टेट ऑब्जेक्ट उपलब्ध कराता है. इसके अलावा, इसमें firebase_ui_auth पैकेज से पुष्टि करने वाले विजेट शामिल होते हैं.

इस चरण के लिए, ApplicationState ऐप्लिकेशन स्टेट ऑब्जेक्ट की एक मुख्य ज़िम्मेदारी होती है. यह विजेट ट्री को सूचना देता है कि पुष्टि की गई स्थिति में कोई अपडेट हुआ है.

ऐप्लिकेशन को यह बताने के लिए कि उपयोगकर्ता ने लॉग इन किया है या नहीं, सिर्फ़ एक प्रोवाइडर का इस्तेमाल किया जाता है. किसी उपयोगकर्ता को लॉग इन करने की अनुमति देने के लिए, firebase_ui_auth पैकेज से मिले यूज़र इंटरफ़ेस (यूआई) का इस्तेमाल किया जाता है. यह आपके ऐप्लिकेशन में लॉग इन स्क्रीन को तुरंत बूटस्ट्रैप करने का एक बेहतरीन तरीका है.

पुष्टि करने की प्रोसेस को इंटिग्रेट करना

  1. lib/main.dart फ़ाइल में सबसे ऊपर मौजूद इंपोर्ट में बदलाव करें:

lib/main.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart'; // new
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';               // new
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';                 // new

import 'app_state.dart';                                 // new
import 'home_page.dart';
  1. ऐप्लिकेशन की स्थिति को ऐप्लिकेशन के शुरू होने से कनेक्ट करें. इसके बाद, HomePage में पुष्टि करने का फ़्लो जोड़ें:

lib/main.dart

void main() {
  // Modify from here...
  WidgetsFlutterBinding.ensureInitialized();

  runApp(ChangeNotifierProvider(
    create: (context) => ApplicationState(),
    builder: ((context, child) => const App()),
  ));
  // ...to here.
}

main() फ़ंक्शन में किए गए बदलाव की वजह से, अब प्रोवाइडर पैकेज की यह ज़िम्मेदारी है कि वह ChangeNotifierProvider विजेट के साथ ऐप्लिकेशन स्टेट ऑब्जेक्ट को इंस्टैंशिएट करे. आपने इस खास provider क्लास का इस्तेमाल किया है, क्योंकि ऐप्लिकेशन स्टेट ऑब्जेक्ट, ChangeNotifier क्लास को बढ़ाता है. इससे provider पैकेज को यह पता चलता है कि डिपेंडेंट विजेट को कब फिर से दिखाया जाए.

  1. अपने ऐप्लिकेशन को अपडेट करें, ताकि FirebaseUI की ओर से उपलब्ध कराई गई अलग-अलग स्क्रीन पर नेविगेट किया जा सके. इसके लिए, GoRouter कॉन्फ़िगरेशन बनाएं:

lib/main.dart

// Add GoRouter configuration outside the App class
final _router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomePage(),
      routes: [
        GoRoute(
          path: 'sign-in',
          builder: (context, state) {
            return SignInScreen(
              actions: [
                ForgotPasswordAction(((context, email) {
                  final uri = Uri(
                    path: '/sign-in/forgot-password',
                    queryParameters: <String, String?>{
                      'email': email,
                    },
                  );
                  context.push(uri.toString());
                })),
                AuthStateChangeAction(((context, state) {
                  final user = switch (state) {
                    SignedIn state => state.user,
                    UserCreated state => state.credential.user,
                    _ => null
                  };
                  if (user == null) {
                    return;
                  }
                  if (state is UserCreated) {
                    user.updateDisplayName(user.email!.split('@')[0]);
                  }
                  if (!user.emailVerified) {
                    user.sendEmailVerification();
                    const snackBar = SnackBar(
                        content: Text(
                            'Please check your email to verify your email address'));
                    ScaffoldMessenger.of(context).showSnackBar(snackBar);
                  }
                  context.pushReplacement('/');
                })),
              ],
            );
          },
          routes: [
            GoRoute(
              path: 'forgot-password',
              builder: (context, state) {
                final arguments = state.uri.queryParameters;
                return ForgotPasswordScreen(
                  email: arguments['email'],
                  headerMaxExtent: 200,
                );
              },
            ),
          ],
        ),
        GoRoute(
          path: 'profile',
          builder: (context, state) {
            return ProfileScreen(
              providers: const [],
              actions: [
                SignedOutAction((context) {
                  context.pushReplacement('/');
                }),
              ],
            );
          },
        ),
      ],
    ),
  ],
);
// end of GoRouter configuration

// Change MaterialApp to MaterialApp.router and add the routerConfig
class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Firebase Meetup',
      theme: ThemeData(
        buttonTheme: Theme.of(context).buttonTheme.copyWith(
              highlightColor: Colors.deepPurple,
            ),
        primarySwatch: Colors.deepPurple,
        textTheme: GoogleFonts.robotoTextTheme(
          Theme.of(context).textTheme,
        ),
        visualDensity: VisualDensity.adaptivePlatformDensity,
        useMaterial3: true,
      ),
      routerConfig: _router, // new
    );
  }
}

पुष्टि करने के फ़्लो की नई स्थिति के आधार पर, हर स्क्रीन से अलग-अलग तरह के ऐक्शन जुड़े होते हैं. पुष्टि करने की प्रोसेस में ज़्यादातर बदलावों के बाद, आपको अपनी पसंद की स्क्रीन पर वापस भेज दिया जाता है. यह होम स्क्रीन या प्रोफ़ाइल जैसी कोई दूसरी स्क्रीन हो सकती है.

  1. HomePage क्लास के बिल्ड मेथड में, ऐप्लिकेशन की स्थिति को AuthFunc विजेट के साथ इंटिग्रेट करें:

lib/home_page.dart

import 'package:firebase_auth/firebase_auth.dart' // new
    hide EmailAuthProvider, PhoneAuthProvider;    // new
import 'package:flutter/material.dart';           // new
import 'package:provider/provider.dart';          // new

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

class HomePage extends StatelessWidget {
  const HomePage({super.key});

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

AuthFunc विजेट को इंस्टैंशिएट किया जाता है और इसे Consumer विजेट में रैप किया जाता है. उपभोक्ता विजेट, provider पैकेज का इस्तेमाल करने का सामान्य तरीका है. इसका इस्तेमाल, ऐप्लिकेशन की स्थिति बदलने पर ट्री के कुछ हिस्से को फिर से बनाने के लिए किया जा सकता है. AuthFunc विजेट, ऐसे पूरक विजेट होते हैं जिनकी जांच की जाती है.

पुष्टि करने की प्रोसेस की जांच करना

cdf2d25e436bd48d.png

  1. ऐप्लिकेशन में, SignInScreen शुरू करने के लिए, जवाब दें बटन पर टैप करें.

2a2cd6d69d172369.png

  1. कोई ईमेल पता डालें. अगर आपने पहले से रजिस्टर किया हुआ है, तो सिस्टम आपको पासवर्ड डालने के लिए कहेगा. अगर आपने रजिस्ट्रेशन नहीं किया है, तो सिस्टम आपको रजिस्ट्रेशन फ़ॉर्म भरने के लिए कहेगा.

e5e65065dba36b54.png

  1. गड़बड़ी ठीक करने के तरीके की जांच करने के लिए, छह से कम वर्णों वाला पासवर्ड डालें. अगर आपने रजिस्टर किया है, तो आपको इसके बजाय पासवर्ड दिखेगा.
  2. गड़बड़ी ठीक करने के फ़्लो की जांच करने के लिए, गलत पासवर्ड डालें.
  3. सही पासवर्ड डालें. आपको लॉग इन करने का विकल्प दिखता है. इससे उपयोगकर्ता को लॉग आउट करने का विकल्प मिलता है.

4ed811a25b0cf816.png

6. Firestore में मैसेज लिखना

यह जानकर अच्छा लगा कि उपयोगकर्ता आ रहे हैं, लेकिन आपको मेहमानों को ऐप्लिकेशन में कुछ और करने के लिए देना होगा. अगर वे गेस्टबुक में मैसेज छोड़ सकें, तो क्या होगा? वे बता सकते हैं कि वे इस इवेंट में आने के लिए क्यों उत्साहित हैं या वे किससे मिलना चाहते हैं.

ऐप्लिकेशन में उपयोगकर्ताओं के लिखे गए चैट मैसेज सेव करने के लिए, Firestore का इस्तेमाल किया जाता है.

डेटा मॉडल

Firestore एक NoSQL डेटाबेस है. इसमें सेव किए गए डेटा को कलेक्शन, दस्तावेज़, फ़ील्ड, और सब-कलेक्शन में बांटा जाता है. चैट के हर मैसेज को guestbook कलेक्शन में एक दस्तावेज़ के तौर पर सेव किया जाता है. यह टॉप-लेवल का कलेक्शन होता है.

7c20dc8424bb1d84.png

Firestore में मैसेज जोड़ना

इस सेक्शन में, उपयोगकर्ताओं को डेटाबेस में मैसेज लिखने की सुविधा जोड़ी जाती है. सबसे पहले, फ़ॉर्म फ़ील्ड और 'भेजें' बटन जोड़ा जाता है. इसके बाद, वह कोड जोड़ा जाता है जो इन एलिमेंट को डेटाबेस से कनेक्ट करता है.

  1. guest_book.dart नाम की नई फ़ाइल बनाएं. इसके बाद, मैसेज फ़ील्ड और 'भेजें' बटन के यूज़र इंटरफ़ेस (यूआई) एलिमेंट बनाने के लिए, GuestBook स्टेटफ़ुल विजेट जोड़ें:

lib/guest_book.dart

import 'dart:async';

import 'package:flutter/material.dart';

import 'src/widgets.dart';

class GuestBook extends StatefulWidget {
  const GuestBook({required this.addMessage, super.key});

  final FutureOr<void> Function(String message) addMessage;

  @override
  State<GuestBook> createState() => _GuestBookState();
}

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

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

यहां कुछ ज़रूरी बातें बताई गई हैं. सबसे पहले, आपको एक फ़ॉर्म बनाना होगा, ताकि यह पुष्टि की जा सके कि मैसेज में कॉन्टेंट मौजूद है. अगर कॉन्टेंट मौजूद नहीं है, तो उपयोगकर्ता को गड़बड़ी का मैसेज दिखाया जा सके. किसी फ़ॉर्म को मान्य करने के लिए, GlobalKey की मदद से फ़ॉर्म के पीछे मौजूद फ़ॉर्म की स्थिति को ऐक्सेस करें. कुंजियों और उनके इस्तेमाल के तरीके के बारे में ज़्यादा जानने के लिए, कुंजियों का इस्तेमाल कब करें लेख पढ़ें.

यह भी ध्यान दें कि विजेट किस तरह से लेआउट किए गए हैं. आपके पास Row है, जिसमें TextFormField और StyledButton है. इसमें Row शामिल है. यह भी ध्यान दें कि TextFormField को Expanded विजेट में रैप किया गया है. इससे TextFormField को लाइन में मौजूद अतिरिक्त जगह को भरने के लिए मजबूर किया जाता है. यह जानकारी क्यों ज़रूरी है, इस बारे में ज़्यादा जानने के लिए पाबंदियों के बारे में जानकारी लेख पढ़ें.

अब आपके पास एक ऐसा विजेट है जिसकी मदद से उपयोगकर्ता, मेहमानों की सूची में जोड़ने के लिए कुछ टेक्स्ट डाल सकता है. अब आपको इसे स्क्रीन पर दिखाना होगा.

  1. 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)),

विजेट दिखाने के लिए यह काफ़ी है, लेकिन इससे कोई काम का फ़ंक्शन नहीं किया जा सकता. इस कोड को जल्द ही अपडेट करें, ताकि यह काम कर सके.

ऐप्लिकेशन की झलक

Android पर, चैट इंटिग्रेशन के साथ ऐप्लिकेशन की होम स्क्रीन

iOS पर ऐप्लिकेशन की होम स्क्रीन, जिसमें चैट इंटिग्रेशन की सुविधा दिख रही है

वेब पर, चैट इंटिग्रेशन वाले ऐप्लिकेशन की होम स्क्रीन

macOS पर, चैट इंटिग्रेशन के साथ ऐप्लिकेशन की होम स्क्रीन

जब कोई उपयोगकर्ता भेजें पर क्लिक करता है, तो यह कोड स्निपेट ट्रिगर होता है. यह मैसेज इनपुट फ़ील्ड के कॉन्टेंट को डेटाबेस के guestbook कलेक्शन में जोड़ता है. खास तौर पर, addMessageToGuestBook तरीके से मैसेज के कॉन्टेंट को एक नए दस्तावेज़ में जोड़ा जाता है. इस दस्तावेज़ का आईडी, guestbook कलेक्शन में अपने-आप जनरेट होता है.

ध्यान दें कि FirebaseAuth.instance.currentUser.uid, अपने-आप जनरेट होने वाले उस यूनीक आईडी का रेफ़रंस है जो Authentication, लॉग-इन किए हुए सभी उपयोगकर्ताओं के लिए देता है.

  • lib/app_state.dart फ़ाइल में, addMessageToGuestBook तरीका जोड़ें. अगले चरण में, इस सुविधा को यूज़र इंटरफ़ेस से कनेक्ट किया जाता है.

lib/app_state.dart

import 'package:cloud_firestore/cloud_firestore.dart'; // new
import 'package:firebase_auth/firebase_auth.dart'
    hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'firebase_options.dart';

class ApplicationState extends ChangeNotifier {

  // Current content of ApplicationState elided ...

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

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

कनेक्ट यूज़र इंटरफ़ेस (यूआई) और डेटाबेस

आपके पास एक यूज़र इंटरफ़ेस (यूआई) है, जहां उपयोगकर्ता गेस्ट बुक में जोड़ने के लिए टेक्स्ट डाल सकता है. साथ ही, आपके पास Firestore में एंट्री जोड़ने का कोड है. अब आपको सिर्फ़ इन दोनों को कनेक्ट करना है.

  • lib/home_page.dart फ़ाइल में, HomePage विजेट में यह बदलाव करें:

lib/home_page.dart

import 'package:firebase_auth/firebase_auth.dart'
    hide EmailAuthProvider, PhoneAuthProvider;
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'app_state.dart';
import 'guest_book.dart';                         // new
import 'src/authentication.dart';
import 'src/widgets.dart';

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

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

आपने इस चरण की शुरुआत में जोड़ी गई दो लाइनों को पूरे कोड से बदल दिया है. ऐप्लिकेशन की स्थिति को ट्री के उस हिस्से के लिए उपलब्ध कराने के लिए, फिर से Consumer<ApplicationState> का इस्तेमाल करें जिसे आपको रेंडर करना है. इसकी मदद से, यूज़र इंटरफ़ेस (यूआई) में मैसेज डालने वाले किसी व्यक्ति को जवाब दिया जा सकता है. साथ ही, उस मैसेज को डेटाबेस में पब्लिश किया जा सकता है. अगले सेक्शन में, यह टेस्ट किया जाता है कि जोड़े गए मैसेज डेटाबेस में पब्लिश हुए हैं या नहीं.

मैसेज भेजने की सुविधा की जांच करना

  1. अगर ज़रूरी हो, तो ऐप्लिकेशन में साइन इन करें.
  2. कोई मैसेज डालें, जैसे कि Hey there!. इसके बाद, भेजें पर क्लिक करें.

इस कार्रवाई से, मैसेज को आपके Firestore डेटाबेस में लिखा जाता है. हालांकि, आपको अपने Flutter ऐप्लिकेशन में यह मैसेज नहीं दिखेगा. ऐसा इसलिए, क्योंकि आपको अब भी डेटा को वापस पाने की सुविधा लागू करनी होगी. यह काम अगले चरण में किया जाता है. हालांकि, Firebase कंसोल के डेटाबेस डैशबोर्ड में, जोड़े गए मैसेज को guestbook कलेक्शन में देखा जा सकता है. ज़्यादा मैसेज भेजने पर, आपके guestbook कलेक्शन में ज़्यादा दस्तावेज़ जुड़ जाते हैं. उदाहरण के लिए, यहां दिया गया कोड स्निपेट देखें:

713870af0b3b63c.png

7. मैसेज पढ़ना

यह बहुत अच्छा है कि मेहमान डेटाबेस में मैसेज लिख सकते हैं. हालाँकि, वे उन्हें अभी ऐप्लिकेशन में नहीं देख सकते. अब इसे ठीक करने का समय आ गया है!

मैसेज सिंक करना

मैसेज दिखाने के लिए, आपको ऐसे लिसनर जोड़ने होंगे जो डेटा में बदलाव होने पर ट्रिगर हों. इसके बाद, एक ऐसा यूज़र इंटरफ़ेस (यूआई) एलिमेंट बनाना होगा जो नए मैसेज दिखाए. ऐप्लिकेशन की स्थिति में ऐसा कोड जोड़ा जाता है जो ऐप्लिकेशन से जोड़े गए नए मैसेज को सुनता है.

  1. एक नई फ़ाइल guest_book_message.dart बनाएं. इसके बाद, Firestore में सेव किए गए डेटा का स्ट्रक्चर्ड व्यू दिखाने के लिए, इसमें यह क्लास जोड़ें.

lib/guest_book_message.dart

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

  final String name;
  final String message;
}
  1. lib/app_state.dart फ़ाइल में, ये इंपोर्ट जोड़ें:

lib/app_state.dart

import 'dart:async';                                     // new

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart'
    hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'firebase_options.dart';
import 'guest_book_message.dart';                        // new
  1. ApplicationState के उस सेक्शन में यह लाइनें जोड़ें जहां आपने स्टेट और गेटर तय किए हैं:

lib/app_state.dart

  bool _loggedIn = false;
  bool get loggedIn => _loggedIn;

  // Add from here...
  StreamSubscription<QuerySnapshot>? _guestBookSubscription;
  List<GuestBookMessage> _guestBookMessages = [];
  List<GuestBookMessage> get guestBookMessages => _guestBookMessages;
  // ...to here.
  1. ApplicationState के इनिशियलाइज़ेशन सेक्शन में, ये लाइनें जोड़ें. इससे जब कोई उपयोगकर्ता लॉग इन करेगा, तब वह दस्तावेज़ के कलेक्शन के लिए क्वेरी की सदस्यता ले पाएगा. साथ ही, जब वह लॉग आउट करेगा, तब सदस्यता रद्द हो जाएगी:

lib/app_state.dart

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

    FirebaseUIAuth.configureProviders([
      EmailAuthProvider(),
    ]);
    
    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loggedIn = true;
        _guestBookSubscription = FirebaseFirestore.instance
            .collection('guestbook')
            .orderBy('timestamp', descending: true)
            .snapshots()
            .listen((snapshot) {
          _guestBookMessages = [];
          for (final document in snapshot.docs) {
            _guestBookMessages.add(
              GuestBookMessage(
                name: document.data()['name'] as String,
                message: document.data()['text'] as String,
              ),
            );
          }
          notifyListeners();
        });
      } else {
        _loggedIn = false;
        _guestBookMessages = [];
        _guestBookSubscription?.cancel();
      }
      notifyListeners();
    });
  }

यह सेक्शन इसलिए अहम है, क्योंकि इसमें guestbook कलेक्शन के लिए क्वेरी बनाई जाती है. साथ ही, इस कलेक्शन की सदस्यता ली और छोड़ी जाती है. स्ट्रीम को सुना जाता है. इसमें, guestbook कलेक्शन में मौजूद मैसेज की लोकल कैश मेमोरी को फिर से बनाया जाता है. साथ ही, इस सदस्यता का रेफ़रंस भी सेव किया जाता है, ताकि बाद में इससे सदस्यता छोड़ी जा सके. यहां बहुत कुछ हो रहा है. इसलिए, आपको इसे डीबगर में एक्सप्लोर करना चाहिए, ताकि यह पता चल सके कि साफ़ तौर पर समझने के लिए क्या होता है. ज़्यादा जानकारी के लिए, Firestore की मदद से रीयलटाइम अपडेट पाना लेख पढ़ें.

  1. lib/guest_book.dart फ़ाइल में, यह इंपोर्ट जोड़ें:
import 'guest_book_message.dart';
  1. GuestBook विजेट में, कॉन्फ़िगरेशन के हिस्से के तौर पर मैसेज की सूची जोड़ें, ताकि इस बदलती स्थिति को यूज़र इंटरफ़ेस से कनेक्ट किया जा सके:

lib/guest_book.dart

class GuestBook extends StatefulWidget {
  // Modify the following line:
  const GuestBook({
    super.key, 
    required this.addMessage, 
    required this.messages,
  });

  final FutureOr<void> Function(String message) addMessage;
  final List<GuestBookMessage> messages; // new

  @override
  _GuestBookState createState() => _GuestBookState();
}
  1. इस कॉन्फ़िगरेशन को दिखाने के लिए, _GuestBookState में build तरीके में इस तरह बदलाव करें:

lib/guest_book.dart

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

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

build() तरीके के पिछले कॉन्टेंट को Column विजेट में रैप किया जाता है. इसके बाद, Column के बच्चों के आखिर में collection for जोड़ा जाता है, ताकि मैसेज की सूची में मौजूद हर मैसेज के लिए एक नया Paragraph जनरेट किया जा सके.

  1. HomePage के मुख्य हिस्से को अपडेट करें, ताकि नए messages पैरामीटर के साथ GuestBook को सही तरीके से बनाया जा सके:

lib/home_page.dart

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

मैसेज सिंक करने की सुविधा की जांच करना

Firestore, डेटाबेस के लिए सदस्यता लेने वाले क्लाइंट के साथ डेटा को अपने-आप और तुरंत सिंक करता है.

मैसेज सिंक करने की सुविधा की जांच करना:

  1. ऐप्लिकेशन में, उन मैसेज को ढूंढें जिन्हें आपने डेटाबेस में पहले बनाया था.
  2. नए मैसेज लिखो. ये तुरंत दिखते हैं.
  3. अपने फ़ाइल फ़ोल्डर को एक से ज़्यादा विंडो या टैब में खोलें. मैसेज, विंडो और टैब में रीयल टाइम में सिंक होते हैं.
  4. वैकल्पिक: Firebase कंसोल के डेटाबेस मेन्यू में जाकर, मैन्युअल तरीके से नए मैसेज जोड़ें, मौजूदा मैसेज में बदलाव करें या उन्हें मिटाएं. सभी बदलाव यूज़र इंटरफ़ेस (यूआई) में दिखते हैं.

बधाई हो! आपने अपने ऐप्लिकेशन में Firestore के दस्तावेज़ पढ़े!

ऐप्लिकेशन की झलक

Android पर, चैट इंटिग्रेशन के साथ ऐप्लिकेशन की होम स्क्रीन

iOS पर ऐप्लिकेशन की होम स्क्रीन, जिसमें चैट इंटिग्रेशन की सुविधा दिख रही है

वेब पर, चैट इंटिग्रेशन वाले ऐप्लिकेशन की होम स्क्रीन

macOS पर, चैट इंटिग्रेशन के साथ ऐप्लिकेशन की होम स्क्रीन

8. सुरक्षा से जुड़े बुनियादी नियम सेट अप करना

आपने शुरुआत में, टेस्ट मोड का इस्तेमाल करने के लिए Firestore सेट अप किया था. इसका मतलब है कि आपका डेटाबेस, पढ़ने और लिखने के लिए खुला है. हालांकि, आपको डेवलपमेंट के शुरुआती चरणों में ही टेस्ट मोड का इस्तेमाल करना चाहिए. सबसे सही तरीका यह है कि ऐप्लिकेशन डेवलप करते समय, अपने डेटाबेस के लिए सुरक्षा से जुड़े नियम सेट अप करें. सुरक्षा, आपके ऐप्लिकेशन के स्ट्रक्चर और व्यवहार का अहम हिस्सा है.

Firebase के सुरक्षा नियमों की मदद से, अपने डेटाबेस में मौजूद दस्तावेज़ों और कलेक्शन का ऐक्सेस कंट्रोल किया जा सकता है. नियमों के सिंटैक्स का इस्तेमाल करके, ऐसे नियम बनाए जा सकते हैं जो सभी राइट ऑपरेशन, पूरे डेटाबेस, और किसी खास दस्तावेज़ पर किए जाने वाले ऑपरेशन से मेल खाते हों.

सुरक्षा से जुड़े बुनियादी नियम सेट अप करें:

  1. Firebase कंसोल के Develop मेन्यू में, Database > Rules पर क्लिक करें. आपको सुरक्षा के ये डिफ़ॉल्ट नियम और नियमों के सार्वजनिक होने के बारे में चेतावनी दिखेगी:

7767a2d2e64e7275.png

  1. उन कलेक्शन की पहचान करें जिनमें ऐप्लिकेशन डेटा लिखता है:

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

आपने हर guestbook दस्तावेज़ में, पुष्टि करने वाले यूआईडी को फ़ील्ड के तौर पर इस्तेमाल किया है. इसलिए, आपको पुष्टि करने वाला यूआईडी मिल सकता है. साथ ही, यह पुष्टि की जा सकती है कि दस्तावेज़ में लिखने की कोशिश करने वाले किसी भी व्यक्ति के पास, पुष्टि करने वाला यूआईडी मौजूद है.

  1. अपने नियमों के सेट में, पढ़ने और लिखने के नियम जोड़ें:
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;
    }
  }
}

अब, मेहमानों की किताब में सिर्फ़ वे लोग मैसेज पढ़ सकते हैं जिन्होंने साइन इन किया है. हालांकि, किसी मैसेज में सिर्फ़ उसका लेखक बदलाव कर सकता है.

  1. डेटा की पुष्टि करने की सुविधा जोड़ें, ताकि यह पक्का किया जा सके कि दस्तावेज़ में सभी ज़रूरी फ़ील्ड मौजूद हैं:
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. बोनस चरण: आपने जो सीखा है उसकी प्रैक्टिस करें

मीटिंग में शामिल होने वाले व्यक्ति के जवाब का स्टेटस रिकॉर्ड करना

फ़िलहाल, आपका ऐप्लिकेशन लोगों को सिर्फ़ तब चैट करने की अनुमति देता है, जब उनकी इवेंट में दिलचस्पी हो. इसके अलावा, आपको सिर्फ़ तब पता चलेगा कि कोई व्यक्ति आ रहा है, जब वह चैट में इसकी जानकारी देगा.

इस चरण में, आपको व्यवस्थित तरीके से काम करना होता है. साथ ही, लोगों को यह बताना होता है कि कितने लोग आ रहे हैं. ऐप्लिकेशन की स्थिति में कुछ सुविधाएं जोड़ी जाती हैं. पहला विकल्प यह है कि लॉग इन किया हुआ उपयोगकर्ता, यह तय कर सकता है कि वह इवेंट में शामिल हो रहा है या नहीं. दूसरा काउंटर, इवेंट में शामिल होने वाले लोगों की संख्या दिखाता है.

  1. lib/app_state.dart फ़ाइल में, ApplicationState के ऐक्सेस करने वाले सेक्शन में ये लाइनें जोड़ें, ताकि यूज़र इंटरफ़ेस (यूआई) कोड इस स्थिति के साथ इंटरैक्ट कर सके:

lib/app_state.dart

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

Attending _attending = Attending.unknown;
StreamSubscription<DocumentSnapshot>? _attendingSubscription;
Attending get attending => _attending;
set attending(Attending attending) {
  final userDoc = FirebaseFirestore.instance
      .collection('attendees')
      .doc(FirebaseAuth.instance.currentUser!.uid);
  if (attending == Attending.yes) {
    userDoc.set(<String, dynamic>{'attending': true});
  } else {
    userDoc.set(<String, dynamic>{'attending': false});
  }
}
  1. ApplicationState के init() के तरीके को इस तरह अपडेट करें:

lib/app_state.dart

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

    FirebaseUIAuth.configureProviders([
      EmailAuthProvider(),
    ]);

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

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

यह कोड, हमेशा सदस्यता लेने वाली क्वेरी जोड़ता है, ताकि शामिल होने वाले लोगों की संख्या का पता लगाया जा सके. साथ ही, यह एक दूसरी क्वेरी जोड़ता है, जो सिर्फ़ तब सक्रिय होती है, जब कोई उपयोगकर्ता लॉग इन होता है. इससे यह पता चलता है कि उपयोगकर्ता शामिल हो रहा है या नहीं.

  1. lib/app_state.dart फ़ाइल में सबसे ऊपर, यहां दिया गया एन्यूमरेशन जोड़ें.

lib/app_state.dart

enum Attending { yes, no, unknown }
  1. नई फ़ाइल yes_no_selection.dart बनाएं. साथ ही, एक नया विजेट तय करें, जो रेडियो बटन की तरह काम करता हो:

lib/yes_no_selection.dart

import 'package:flutter/material.dart';

import 'app_state.dart';
import 'src/widgets.dart';

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

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

यह अनिश्चित स्थिति में शुरू होता है. इसमें न तो हां चुना जाता है और न ही नहीं. जब उपयोगकर्ता यह चुन लेता है कि वह इवेंट में शामिल हो रहा है या नहीं, तो आपको उस विकल्प को हाइलाइट करना होगा. इसके लिए, भरे हुए बटन का इस्तेमाल करें. साथ ही, दूसरे विकल्प को फ़्लैट रेंडरिंग के साथ दिखाना होगा.

  1. YesNoSelection का फ़ायदा पाने के लिए, HomePage के build() तरीके को अपडेट करें. इससे लॉग इन किए हुए उपयोगकर्ता को यह चुनने की सुविधा मिलती है कि वह इवेंट में शामिल हो रहा है या नहीं. साथ ही, इवेंट में शामिल होने वाले लोगों की संख्या भी दिखती है:

lib/home_page.dart

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

नियम जोड़ें

आपने पहले से ही कुछ नियम सेट अप किए हैं. इसलिए, बटन की मदद से जोड़े गए डेटा को अस्वीकार कर दिया जाएगा. आपको attendees कलेक्शन में आइटम जोड़ने की अनुमति देने के लिए, नियमों को अपडेट करना होगा.

  1. attendees कलेक्शन में, दस्तावेज़ के नाम के तौर पर इस्तेमाल किया गया पुष्टि करने वाला यूआईडी पाएं. साथ ही, पुष्टि करें कि सबमिट करने वाले का 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;
    }
  }
}

इससे सभी लोग, शामिल होने वाले लोगों की सूची को पढ़ सकते हैं, क्योंकि इसमें कोई निजी डेटा नहीं होता. हालांकि, इसे सिर्फ़ क्रिएटर अपडेट कर सकता है.

  1. डेटा की पुष्टि करने की सुविधा जोड़ें, ताकि यह पक्का किया जा सके कि दस्तावेज़ में सभी ज़रूरी फ़ील्ड मौजूद हैं:
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;

    }
  }
}
  1. ज़रूरी नहीं: ऐप्लिकेशन में, बटन पर क्लिक करके Firebase कंसोल में Firestore डैशबोर्ड में नतीजे देखें.

ऐप्लिकेशन की झलक

Android पर ऐप्लिकेशन की होम स्क्रीन

iOS पर ऐप्लिकेशन की होम स्क्रीन

वेब पर ऐप्लिकेशन की होम स्क्रीन

macOS पर ऐप्लिकेशन की होम स्क्रीन

10. बधाई हो!

आपने Firebase का इस्तेमाल करके, एक इंटरैक्टिव और रीयल-टाइम वेब ऐप्लिकेशन बनाया है!

ज़्यादा जानें