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 सीएलआई ने आपके 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

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

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

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

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

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

  • ये फ़ाइलें ढूंढें:
    • lib/main.dart: इस फ़ाइल में मुख्य एंट्री पॉइंट और ऐप्लिकेशन विजेट शामिल होता है.
    • lib/home_page.dart: इस फ़ाइल में होम पेज विजेट शामिल होता है.
    • lib/src/widgets.dart: इस फ़ाइल में कुछ विजेट होते हैं, जिनसे ऐप्लिकेशन के स्टाइल को स्टैंडर्ड बनाने में मदद मिलती है. ये विजेट, स्टार्टर ऐप्लिकेशन की स्क्रीन को कॉम्पोज़ करते हैं.
    • lib/src/authentication.dart: इस फ़ाइल में, पुष्टि करने की सुविधा को कुछ हद तक लागू किया गया है. इसमें विजेट का एक सेट भी शामिल है, ताकि 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. Firebase में साइन इन करें.
  2. कंसोल में, प्रोजेक्ट जोड़ें या प्रोजेक्ट बनाएं पर क्लिक करें.
  3. प्रोजेक्ट का नाम फ़ील्ड में, Firebase-Flutter-Codelab डालें. इसके बाद, जारी रखें पर क्लिक करें.

4395e4e67c08043a.png

  1. प्रोजेक्ट बनाने के विकल्पों पर क्लिक करें. अगर कहा जाए, तो Firebase की शर्तें स्वीकार करें. हालांकि, Google Analytics का सेटअप न करें, क्योंकि आपको इस ऐप्लिकेशन के लिए इसका इस्तेमाल नहीं करना है.

b7138cde5f2c7b61.png

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 का इस्तेमाल करने के लिए, आपको Flutter प्रोजेक्ट को कॉन्फ़िगर करना होगा, ताकि FlutterFire लाइब्रेरी का सही तरीके से इस्तेमाल किया जा सके. इसके लिए, आपको ये काम करने होंगे:

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

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

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

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

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

firebase_core पैकेज, सभी 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 सीएलआई इंस्टॉल करें:
$ 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 जोड़ने के बाद, हां में जवाब देने के लिए एक बटन बनाया जा सकता है. इस बटन की मदद से, लोग पुष्टि करके रजिस्टर कर सकते हैं. Android नेटिव, iOS नेटिव, और वेब के लिए, पहले से बने FirebaseUI Auth पैकेज उपलब्ध हैं. हालांकि, आपको Flutter के लिए यह सुविधा बनानी होगी.

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

Provider पैकेज के साथ कारोबारी लॉजिक जोड़ना

Flutter विजेट के ऐप्लिकेशन के ट्री में, ऐप्लिकेशन स्टेटस का एक सेंट्रलाइज़्ड ऑब्जेक्ट उपलब्ध कराने के लिए, 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. GoRouter कॉन्फ़िगरेशन बनाकर, अपने ऐप्लिकेशन को अपडेट करें, ताकि वह FirebaseUI की अलग-अलग स्क्रीन पर नेविगेट कर सके:

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 शुरू करने के लिए, ऐप्लिकेशन में RSVP बटन पर टैप करें.

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 का इस्तेमाल करके फ़ॉर्म के पीछे मौजूद फ़ॉर्म की स्थिति को ऐक्सेस करें. पासकोड और उन्हें इस्तेमाल करने के तरीके के बारे में ज़्यादा जानने के लिए, पासकोड का इस्तेमाल कब करना चाहिए लेख पढ़ें.

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

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

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

  • 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 विजेट के साथ रैप किया जाता है. इसके बाद, मैसेज की सूची में हर मैसेज के लिए नया Paragraph जनरेट करने के लिए, Column के चाइल्ड के आखिर में इसके लिए कलेक्शन जोड़ा जाता है.

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

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

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

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

  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, उस दस्तावेज़ से मेल खाता हो जिसे वह लिख रहा है:
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 का इस्तेमाल किया है!

और जानें