फायरबेस क्रॉस डिवाइस कोडेलैब

1 परिचय

अंतिम अद्यतन: 2022-03-14

क्रॉस डिवाइस संचार के लिए फ़्लटरफ़ायर

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

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

आप क्या बनाएंगे

इस कोडलैब में, आप एक साधारण म्यूजिक प्लेयर रिमोट कंट्रोलर बनाएंगे। आपका ऐप होगा:

  • एंड्रॉइड, आईओएस और वेब पर फ़्लटर के साथ निर्मित एक साधारण म्यूजिक प्लेयर रखें।
  • उपयोगकर्ताओं को साइन इन करने की अनुमति दें.
  • जब एक ही उपयोगकर्ता एकाधिक डिवाइस पर साइन इन हो तो डिवाइस कनेक्ट करें।
  • उपयोगकर्ताओं को एक डिवाइस से दूसरे डिवाइस पर संगीत प्लेबैक को नियंत्रित करने की अनुमति दें।

7f0279938e1d3ab5.gif

आप क्या सीखेंगे

  • फ़्लटर म्यूज़िक प्लेयर ऐप कैसे बनाएं और चलाएं।
  • उपयोगकर्ताओं को फ़ायरबेस ऑथ के साथ साइन इन करने की अनुमति कैसे दें।
  • डिवाइस कनेक्ट करने के लिए फायरबेस आरटीडीबी प्रेजेंस एपीआई और फायरबेस इंस्टॉलेशन सर्विस का उपयोग कैसे करें।

आपको किस चीज़ की ज़रूरत पड़ेगी

  • एक स्पंदन विकास वातावरण। इसे सेट करने के लिए फ़्लटर इंस्टॉलेशन गाइड में दिए गए निर्देशों का पालन करें।
  • 2.10 या उच्चतर का न्यूनतम फ़्लटर संस्करण आवश्यक है। यदि आपके पास निचला संस्करण है, तो flutter upgrade.
  • एक फायरबेस खाता.

2. स्थापित होना

स्टार्टर कोड प्राप्त करें

हमने फ़्लटर में एक म्यूज़िक प्लेयर ऐप बनाया है। स्टार्टर कोड Git रेपो में स्थित है। आरंभ करने के लिए, कमांड लाइन पर, रेपो को क्लोन करें, शुरुआती स्थिति वाले फ़ोल्डर में जाएं और निर्भरताएं स्थापित करें:

git clone https://github.com/FirebaseExtended/cross-device-controller.git

cd cross-device-controller/starter_code

flutter pub get

ऐप बनाएं

आप ऐप बनाने के लिए अपने पसंदीदा आईडीई के साथ काम कर सकते हैं, या कमांड लाइन का उपयोग कर सकते हैं।

अपनी ऐप निर्देशिका में, flutter run -d web-server. आपको निम्नलिखित संकेत देखने में सक्षम होना चाहिए।

lib/main.dart is being served at http://localhost:<port>

म्यूजिक प्लेयर देखने के लिए http://localhost:<port> पर जाएं।

यदि आप एंड्रॉइड एमुलेटर या आईओएस सिम्युलेटर से परिचित हैं, तो आप उन प्लेटफार्मों के लिए ऐप बना सकते हैं और इसे flutter run -d <device_name> कमांड के साथ इंस्टॉल कर सकते हैं।

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

एक एंड्रॉइड एमुलेटर या एक आईओएस सिम्युलेटर सेट करें

यदि आपके पास विकास के लिए पहले से ही एंड्रॉइड डिवाइस या आईओएस डिवाइस है, तो आप इस चरण को छोड़ सकते हैं।

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

iOS सिम्युलेटर बनाने के लिए, आपको Mac वातावरण की आवश्यकता होगी। XCode डाउनलोड करें, और सिम्युलेटर अवलोकन > सिम्युलेटर का उपयोग करें > सिम्युलेटर खोलें और बंद करें में दिए गए निर्देशों का पालन करें।

3. फायरबेस की स्थापना

एक फायरबेस प्रोजेक्ट बनाएं

http://console.firebase.google.com/ पर एक ब्राउज़र खोलें।

  1. फायरबेस में साइन इन करें।
  2. फायरबेस कंसोल में, प्रोजेक्ट जोड़ें (या प्रोजेक्ट बनाएं ) पर क्लिक करें, और अपने फायरबेस प्रोजेक्ट को फायरबेस-क्रॉस-डिवाइस-कोडेलैब नाम दें।
  3. प्रोजेक्ट निर्माण विकल्पों पर क्लिक करें। संकेत मिलने पर फायरबेस शर्तों को स्वीकार करें। Google Analytics सेट करना छोड़ें, क्योंकि आप इस ऐप के लिए Analytics का उपयोग नहीं करेंगे।

आपको उल्लिखित फ़ाइलों को डाउनलोड करने या बिल्ड.ग्रेडल फ़ाइलों को बदलने की आवश्यकता नहीं है। जब आप फ़्लटरफ़ायर आरंभ करेंगे तो आप उन्हें कॉन्फ़िगर करेंगे।

फायरबेस एसडीके स्थापित करें

कमांड लाइन पर वापस, प्रोजेक्ट डायरेक्टरी में, फायरबेस स्थापित करने के लिए निम्नलिखित कमांड चलाएँ:

flutter pub add firebase_core

pubspec.yaml फ़ाइल में, firebase_core के लिए संस्करण को कम से कम 1.13.1 संपादित करें, या flutter upgrade चलाएँ

फ़्लटरफ़ायर आरंभ करें

  1. यदि आपके पास फायरबेस सीएलआई स्थापित नहीं है, तो आप इसे curl -sL https://firebase.tools | bash चलाकर स्थापित कर सकते हैं। curl -sL https://firebase.tools | bash
  2. firebase login चलाकर और संकेतों का पालन करके लॉग इन करें।
  3. dart pub global activate flutterfire_cli चलाकर फ़्लटरफ़ायर सीएलआई स्थापित करें।
  4. flutterfire configure कॉन्फ़िगर चलाकर फ़्लटरफ़ायर सीएलआई कॉन्फ़िगर करें।
  5. प्रॉम्प्ट पर, वह प्रोजेक्ट चुनें जिसे आपने अभी इस कोडेलैब के लिए बनाया है, कुछ इस तरह कि फायरबेस-क्रॉस-डिवाइस-कोडेलैब
  6. जब आपसे कॉन्फ़िगरेशन समर्थन चुनने के लिए कहा जाए तो iOS , Android और वेब का चयन करें।
  7. जब Apple बंडल आईडी के लिए कहा जाए, तो एक अद्वितीय डोमेन टाइप करें, या com.example.appname दर्ज करें, जो इस कोडलैब के उद्देश्य के लिए ठीक है।

एक बार कॉन्फ़िगर हो जाने पर, आपके लिए एक firebase_options.dart फ़ाइल तैयार की जाएगी जिसमें आरंभीकरण के लिए आवश्यक सभी विकल्प होंगे।

अपने संपादक में, फ़्लटर और फ़ायरबेस को आरंभ करने के लिए अपनी मुख्य.डार्ट फ़ाइल में निम्नलिखित कोड जोड़ें:

lib/main.dart

import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
 
void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Firebase.initializeApp(
   options: DefaultFirebaseOptions.currentPlatform,
 );
 runApp(const MyMusicBoxApp());
}

एप्लिकेशन को कमांड के साथ संकलित करें:

flutter run

आपने अभी तक कोई यूआई तत्व नहीं बदला है, इसलिए ऐप का स्वरूप और व्यवहार नहीं बदला है। लेकिन अब आपके पास एक फायरबेस ऐप है, और आप फायरबेस उत्पादों का उपयोग शुरू कर सकते हैं, जिनमें शामिल हैं:

  • फायरबेस प्रमाणीकरण , जो आपके उपयोगकर्ताओं को आपके ऐप में साइन इन करने की अनुमति देता है।
  • फायरबेस रीयलटाइम डेटाबेस (आरटीडीबी) ; आप डिवाइस की ऑनलाइन/ऑफ़लाइन स्थिति ट्रैक करने के लिए उपस्थिति API का उपयोग करेंगे
  • फायरबेस सुरक्षा नियम आपको डेटाबेस को सुरक्षित करने देंगे।
  • फायरबेस इंस्टालेशन सेवा उन डिवाइसों की पहचान करने के लिए है जिनमें किसी एकल उपयोगकर्ता ने साइन इन किया है।

4. फायरबेस ऑथेंटिक जोड़ें

फायरबेस प्रमाणीकरण के लिए ईमेल साइन-इन सक्षम करें

उपयोगकर्ताओं को वेब ऐप में साइन इन करने की अनुमति देने के लिए, आप ईमेल/पासवर्ड साइन-इन विधि का उपयोग करेंगे:

  1. फायरबेस कंसोल में, बाएं पैनल में बिल्ड मेनू का विस्तार करें।
  2. प्रमाणीकरण पर क्लिक करें, और फिर प्रारंभ करें बटन पर क्लिक करें, फिर साइन-इन विधि टैब पर क्लिक करें।
  3. साइन-इन प्रदाताओं की सूची में ईमेल/पासवर्ड पर क्लिक करें, सक्षम स्विच को चालू स्थिति पर सेट करें और फिर सहेजें पर क्लिक करें। 58e3e3e23c2f16a4.png

फ़्लटर में फ़ायरबेस प्रमाणीकरण कॉन्फ़िगर करें

कमांड लाइन पर, आवश्यक फ़्लटर पैकेज स्थापित करने के लिए निम्नलिखित कमांड चलाएँ:

flutter pub add firebase_auth

flutter pub add provider

इस कॉन्फ़िगरेशन के साथ, अब आप साइन-इन और साइन-आउट प्रवाह बना सकते हैं। चूँकि प्रमाणीकरण स्थिति एक स्क्रीन से दूसरी स्क्रीन में नहीं बदलनी चाहिए, आप ऐप स्तर की स्थिति में बदलाव, जैसे लॉग इन और लॉग आउट, पर नज़र रखने के लिए एक application_state.dart क्लास बनाएंगे। फ़्लटर स्थिति प्रबंधन दस्तावेज़ में इसके बारे में और जानें।

निम्नलिखित को नई application_state.dart फ़ाइल में चिपकाएँ:

lib/src/application_state.dart

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

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

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

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

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

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

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

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

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

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

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

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

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

यह सुनिश्चित करने के लिए कि ऐप शुरू होने पर ApplicationState प्रारंभ हो जाएगा, आप main.dart में एक प्रारंभिक चरण जोड़ेंगे:

lib/main.dart

import 'src/application_state.dart'; 
import 'package:provider/provider.dart';

void main() async {
  ... 
  runApp(ChangeNotifierProvider(
    create: (context) => ApplicationState(),
    builder: (context, _) => const MyMusicBoxApp(),
  ));
}

फिर, एप्लिकेशन यूआई वही रहना चाहिए था, लेकिन अब आप उपयोगकर्ताओं को साइन इन करने और ऐप स्थिति सहेजने दे सकते हैं।

एक साइन इन फ्लो बनाएं

इस चरण में, आप साइन इन और साइन आउट प्रवाह पर काम करेंगे। यहाँ प्रवाह इस प्रकार दिखेगा:

  1. लॉग आउट किया गया उपयोगकर्ता संदर्भ मेनू पर क्लिक करके साइन-इन प्रवाह आरंभ करेगा 71fcc1030a336423.png ऐप बार के दाईं ओर।
  2. साइन-इन प्रवाह एक संवाद में प्रदर्शित किया जाएगा।
  3. यदि उपयोगकर्ता ने पहले कभी साइन इन नहीं किया है, तो उन्हें एक वैध ईमेल पते और पासवर्ड का उपयोग करके एक खाता बनाने के लिए प्रेरित किया जाएगा।
  4. यदि उपयोगकर्ता ने पहले साइन इन किया है, तो उन्हें अपना पासवर्ड दर्ज करने के लिए कहा जाएगा।
  5. एक बार जब उपयोगकर्ता साइन इन हो जाएगा, तो संदर्भ मेनू पर क्लिक करने पर साइन आउट विकल्प दिखाई देगा।

c295f6fa2e1d40f3.png

साइन-इन प्रवाह जोड़ने के लिए तीन चरणों की आवश्यकता होती है।

सबसे पहले, एक AppBarMenuButton विजेट बनाएं। यह विजेट उपयोगकर्ता के loginState के आधार पर संदर्भ मेनू पॉपअप को नियंत्रित करेगा। आयात जोड़ें

lib/src/widgets.dart

import 'application_state.dart';
import 'package:provider/provider.dart';
import 'authentication.dart';

निम्नलिखित कोड को widgets.dart.

lib/src/widgets.dart

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

  @override
  Widget build(BuildContext context) {
    return Consumer<ApplicationState>(
      builder: (context, appState, child) {
        if (appState.loginState == ApplicationLoginState.loggedIn) {
          return SignedInMenuButton(buildContext: context);
        }
        return SignInMenuButton(buildContext: context);
      },
    );
  }
}

class SignedInMenuButton extends StatelessWidget {
  const SignedInMenuButton({Key? key, required this.buildContext})
      : super(key: key);
  final BuildContext buildContext;

  @override
  Widget build(BuildContext context) {
    return PopupMenuButton<String>(
      onSelected: _handleSignedInMenu,
      color: Colors.deepPurple.shade300,
      itemBuilder: (context) => _getMenuItemBuilder(),
    );
  }

  List<PopupMenuEntry<String>> _getMenuItemBuilder() {
    return [
      const PopupMenuItem<String>(
        value: 'Sign out',
        child: Text(
          'Sign out',
          style: TextStyle(color: Colors.white),
        ),
      )
    ];
  }

  Future<void> _handleSignedInMenu(String value) async {
    switch (value) {
      case 'Sign out':
        Provider.of<ApplicationState>(buildContext, listen: false).signOut();
        break;
    }
  }
}

class SignInMenuButton extends StatelessWidget {
  const SignInMenuButton({Key? key, required this.buildContext})
      : super(key: key);
  final BuildContext buildContext;

  @override
  Widget build(BuildContext context) {
    return PopupMenuButton<String>(
      onSelected: _signIn,
      color: Colors.deepPurple.shade300,
      itemBuilder: (context) => _getMenuItemBuilder(context),
    );
  }

  Future<void> _signIn(String value) async {
    return showDialog<void>(
      context: buildContext,
      builder: (context) => const SignInDialog(),
    );
  }

  List<PopupMenuEntry<String>> _getMenuItemBuilder(BuildContext context) {
    return [
      const PopupMenuItem<String>(
        value: 'Sign in',
        child: Text(
          'Sign in',
          style: TextStyle(color: Colors.white),
        ),
      ),
    ];
  }
}

दूसरा, उसी widgets.dart क्लास में, SignInDialog विजेट बनाएं।

lib/src/widgets.dart

class SignInDialog extends AlertDialog {
  const SignInDialog({Key? key}) : super(key: key);

  @override
  AlertDialog build(BuildContext context) {
    return AlertDialog(
      content: Column(mainAxisSize: MainAxisSize.min, children: [
        Consumer<ApplicationState>(
          builder: (context, appState, _) => Authentication(
            email: appState.email,
            loginState: appState.loginState,
            startLoginFlow: appState.startLoginFlow,
            verifyEmail: appState.verifyEmail,
            signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
            cancelRegistration: appState.cancelRegistration,
            registerAccount: appState.registerAccount,
            signOut: appState.signOut,
          ),
        ),
      ]),
    );
  }
}

तीसरा, main.dart. साइन इन या साइन आउट विकल्प प्रदर्शित करने के लिए AppBarMenuButton जोड़ें।

lib/main.dart

import 'src/widgets.dart';
appBar: AppBar(
  title: const Text('Music Box'),
  backgroundColor: Colors.deepPurple.shade400,
  actions: const <Widget>[
    AppBarMenuButton(),
  ],
),

इन परिवर्तनों के साथ ऐप को पुनरारंभ करने के लिए flutter run कमांड चलाएँ। आपको संदर्भ मेनू देखने में सक्षम होना चाहिए 71fcc1030a336423.png ऐप बार के दाईं ओर। इस पर क्लिक करने पर आप साइन-इन डायलॉग पर पहुंच जाएंगे।

एक बार जब आप वैध ईमेल पते और पासवर्ड के साथ साइन इन कर लेते हैं, तो आपको संदर्भ मेनू में साइन आउट विकल्प देखने में सक्षम होना चाहिए।

फ़ायरबेस कंसोल में, प्रमाणीकरण के अंतर्गत, आपको नए उपयोगकर्ता के रूप में सूचीबद्ध ईमेल पता देखने में सक्षम होना चाहिए।

888506c86a28a72c.png

बधाई हो! उपयोगकर्ता अब ऐप में साइन इन कर सकते हैं!

5. डेटाबेस कनेक्शन जोड़ें

अब आप फायरबेस प्रेजेंस एपीआई का उपयोग करके डिवाइस पंजीकरण के लिए आगे बढ़ने के लिए तैयार हैं।

कमांड लाइन पर, आवश्यक निर्भरताएँ जोड़ने के लिए निम्नलिखित कमांड चलाएँ:

flutter pub add firebase_app_installations

flutter pub add firebase_database

एक डेटाबेस बनाएं

फायरबेस कंसोल में,

  1. फायरबेस कंसोल के रीयलटाइम डेटाबेस अनुभाग पर नेविगेट करें। डेटाबेस बनाएँ पर क्लिक करें।
  2. यदि आपके सुरक्षा नियमों के लिए प्रारंभिक मोड का चयन करने के लिए कहा जाए, तो अभी के लिए टेस्ट मोड का चयन करें**। ** (टेस्ट मोड सुरक्षा नियम बनाता है जो सभी अनुरोधों की अनुमति देता है। आप बाद में सुरक्षा नियम जोड़ देंगे। यह महत्वपूर्ण है कि कभी भी उत्पादन में न जाएं आपके सुरक्षा नियम अभी भी परीक्षण मोड में हैं।)

डेटाबेस अभी खाली है. सामान्य टैब के अंतर्गत प्रोजेक्ट सेटिंग्स में अपना databaseURL ढूंढें। वेब ऐप्स अनुभाग तक नीचे स्क्रॉल करें।

1b6076f60a36263b.png

अपना databaseURL firebase_options.dart फ़ाइल में जोड़ें :

lib/firebase_options.dart

 static const FirebaseOptions web = FirebaseOptions(
    apiKey: yourApiKey,
    ...
    databaseURL: 'https://<YOUR_DATABASE_URL>,
    ...
  );

आरटीडीबी उपस्थिति एपीआई का उपयोग करके डिवाइस पंजीकृत करें

जब उपयोगकर्ता के उपकरण ऑनलाइन दिखाई दें तो आप उन्हें पंजीकृत करना चाहते हैं। ऐसा करने के लिए, आप एकल उपयोगकर्ता के ऑनलाइन उपकरणों की सूची पर नज़र रखने के लिए फ़ायरबेस इंस्टॉलेशन और फ़ायरबेस आरटीडीबी उपस्थिति एपीआई का लाभ उठाएंगे। निम्नलिखित कोड इस लक्ष्य को पूरा करने में मदद करेगा:

lib/src/application_state.dart

import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:firebase_app_installations/firebase_app_installations.dart'; 

class ApplicationState extends ChangeNotifier {

  String? _deviceId;
  String? _uid;

  Future<void> init() async {
    ...
    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
        _uid = user.uid;
        _addUserDevice();
      }
      ...
    });
  }

  Future<void> _addUserDevice() async {
    _uid = FirebaseAuth.instance.currentUser?.uid;

    String deviceType = _getDevicePlatform();
    // Create two objects which we will write to the
    // Realtime database when this device is offline or online
    var isOfflineForDatabase = {
      'type': deviceType,
      'state': 'offline',
      'last_changed': ServerValue.timestamp,
    };
    var isOnlineForDatabase = {
      'type': deviceType,
      'state': 'online',
      'last_changed': ServerValue.timestamp,
    };

    var devicesRef =
        FirebaseDatabase.instance.ref().child('/users/$_uid/devices');

    FirebaseInstallations.instance
        .getId()
        .then((id) => _deviceId = id)
        .then((_) {
      // Use the semi-persistent Firebase Installation Id to key devices
      var deviceStatusRef = devicesRef.child('$_deviceId');

      // RTDB Presence API
      FirebaseDatabase.instance
          .ref()
          .child('.info/connected')
          .onValue
          .listen((data) {
        if (data.snapshot.value == false) {
          return;
        }

        deviceStatusRef.onDisconnect().set(isOfflineForDatabase).then((_) {
          deviceStatusRef.set(isOnlineForDatabase);
        });
      });
    });
  }

  String _getDevicePlatform() {
    if (kIsWeb) {
      return 'Web';
    } else if (Platform.isIOS) {
      return 'iOS';
    } else if (Platform.isAndroid) {
      return 'Android';
    }
    return 'Unknown';
  }

कमांड लाइन पर वापस जाएं, अपने डिवाइस पर या flutter run.

अपने ऐप में, एक उपयोगकर्ता के रूप में साइन इन करें। विभिन्न प्लेटफ़ॉर्म पर एक ही उपयोगकर्ता के रूप में साइन इन करना याद रखें।

फायरबेस कंसोल में, आपको अपने डिवाइस को अपने डेटाबेस में एक उपयोगकर्ता आईडी के अंतर्गत प्रदर्शित होते देखना चाहिए।

5bef49cea3564248.png

6. सिंक डिवाइस स्थिति

एक लीड डिवाइस चुनें

डिवाइसों के बीच स्थितियों को सिंक करने के लिए, एक डिवाइस को लीडर या नियंत्रक के रूप में नामित करें। लीड डिवाइस फ़ॉलोअर डिवाइसों पर स्थितियों को निर्देशित करेगा।

application_state.dart में एक setLeadDevice विधि बनाएं, और आरटीडीबी में active_device कुंजी के साथ इस डिवाइस को ट्रैक करें:

lib/src/application_state.dart

  bool _isLeadDevice = false;
  String? leadDeviceType;

  Future<void> setLeadDevice() async {
    if (_uid != null && _deviceId != null) {
      var playerRef =
          FirebaseDatabase.instance.ref().child('/users/$_uid/active_device');
      await playerRef
          .update({'id': _deviceId, 'type': _getDevicePlatform()}).then((_) {
        _isLeadDevice = true;
      });
    }
  }

इस कार्यक्षमता को ऐप बार संदर्भ मेनू में जोड़ने के लिए, SignedInMenuButton विजेट को संशोधित करके Controller नामक एक PopupMenuItem बनाएं। यह मेनू उपयोगकर्ताओं को लीड डिवाइस सेट करने की अनुमति देगा।

lib/src/widgets.dart

class SignedInMenuButton extends StatelessWidget {
  const SignedInMenuButton({Key? key, required this.buildContext})
      : super(key: key);
  final BuildContext buildContext;

  List<PopupMenuEntry<String>> _getMenuItemBuilder() {
    return [
      const PopupMenuItem<String>(
        value: 'Sign out',
        child: Text(
          'Sign out',
          style: TextStyle(color: Colors.white),
        ),
      ),
      const PopupMenuItem<String>(
        value: 'Controller',
        child: Text(
          'Set as controller',
          style: TextStyle(color: Colors.white),
        ),
      )
    ];
  }

  void _handleSignedInMenu(String value) async {
    switch (value) {
      ...
      case 'Controller':
        Provider.of<ApplicationState>(buildContext, listen: false)
            .setLeadDevice();
    }
  }
}

डेटाबेस में लीड डिवाइस की स्थिति लिखें

एक बार जब आप लीड डिवाइस सेट कर लेते हैं, तो आप निम्नलिखित कोड के साथ लीड डिवाइस की स्थिति को आरटीडीबी में सिंक कर सकते हैं। निम्नलिखित कोड को application_state.dart. यह दो विशेषताओं को संग्रहीत करना शुरू कर देगा: प्लेयर स्थिति (चलाएँ या रोकें) और स्लाइडर स्थिति।

lib/src/application_state.dart

  Future<void> setLeadDeviceState(
      int playerState, double sliderPosition) async {
    if (_isLeadDevice && _uid != null && _deviceId != null) {
      var leadDeviceStateRef =
          FirebaseDatabase.instance.ref().child('/users/$_uid/active_device');
      try {
        var playerSnapshot = {
          'id': _deviceId,
          'state': playerState,
          'type': _getDevicePlatform(),
          'slider_position': sliderPosition
        };
        await leadDeviceStateRef.set(playerSnapshot);
      } catch (e) {
        throw Exception('updated playerState with error');
      }
    }
  }

और अंत में, जब भी नियंत्रक की प्लेयर स्थिति अपडेट होती है तो आपको setActiveDeviceState कॉल करना होगा। मौजूदा player_widget.dart फ़ाइल में निम्नलिखित परिवर्तन करें:

lib/player_widget.dart

import 'package:provider/provider.dart';
import 'application_state.dart';

 void _onSliderChangeHandler(v) {
    ...
    // update player state in RTDB if device is active
    Provider.of<ApplicationState>(context, listen: false)
        .setLeadDeviceState(_playerState.index, _sliderPosition);
 }

 Future<int> _pause() async {
    ...
    // update DB if device is active
    Provider.of<ApplicationState>(context, listen: false)
        .setLeadDeviceState(_playerState.index, _sliderPosition);
    return result;
  }

 Future<int> _play() async {
    var result = 0;

    // update DB if device is active
    Provider.of<ApplicationState>(context, listen: false)
        .setLeadDeviceState(PlayerState.PLAYING.index, _sliderPosition);

    if (_playerState == PlayerState.PAUSED) {
      result = await _audioPlayer.resume();
      return result;
    }
    ...
 }

 Future<int> _updatePositionAndSlider(Duration tempPosition) async {
    ...
    // update DB if device is active
    Provider.of<ApplicationState>(context, listen: false)
        .setLeadDeviceState(_playerState.index, _sliderPosition);
    return result;
  }

डेटाबेस से लीड डिवाइस की स्थिति पढ़ें

लीड डिवाइस की स्थिति को पढ़ने और उपयोग करने के लिए दो भाग हैं। सबसे पहले, आप application_state में लीड प्लेयर स्थिति का डेटाबेस श्रोता सेट करना चाहते हैं। यह श्रोता कॉलबैक के माध्यम से अनुयायी डिवाइस को बताएगा कि स्क्रीन को कब अपडेट करना है। ध्यान दें कि आपने इस चरण में एक इंटरफ़ेस OnLeadDeviceChangeCallback परिभाषित किया है। इसे अभी तक लागू नहीं किया गया है; आप अगले चरण में इस इंटरफ़ेस को player_widget.dart में लागू करेंगे।

lib/src/application_state.dart

// Interface to be implemented by PlayerWidget
typedef OnLeadDeviceChangeCallback = void Function(
    Map<dynamic, dynamic> snapshot);

class ApplicationState extends ChangeNotifier {
  ...

  OnLeadDeviceChangeCallback? onLeadDeviceChangeCallback;

  Future<void> init() async {
    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
        _uid = user.uid;
        _addUserDevice().then((_) => listenToLeadDeviceChange());
      }
      ...
    });
  }

  Future<void> listenToLeadDeviceChange() async {
    if (_uid != null) {
      var activeDeviceRef =
          FirebaseDatabase.instance.ref().child('/users/$_uid/active_device');
      activeDeviceRef.onValue.listen((event) {
        final activeDeviceState = event.snapshot.value as Map<dynamic, dynamic>;
        String activeDeviceKey = activeDeviceState['id'] as String;
        _isLeadDevice = _deviceId == activeDeviceKey;
        leadDeviceType = activeDeviceState['type'] as String;
        if (!_isLeadDevice) {
          onLeadDeviceChangeCallback?.call(activeDeviceState);
        }
        notifyListeners();
      });
    }
  }

दूसरा, player_widget.dart में प्लेयर आरंभीकरण के दौरान डेटाबेस श्रोता प्रारंभ करें। _updatePlayer फ़ंक्शन को पास करें ताकि जब भी डेटाबेस मान में परिवर्तन हो तो फ़ॉलोअर प्लेयर स्थिति को अपडेट किया जा सके।

lib/player_widget.dart

class _PlayerWidgetState extends State<PlayerWidget> {

  @override
  void initState() {
    ...
    Provider.of<ApplicationState>(context, listen: false)
        .onLeadDeviceChangeCallback = updatePlayer;
  }

  void updatePlayer(Map<dynamic, dynamic> snapshot) {
    _updatePlayer(snapshot['state'], snapshot['slider_position']);
  }

  void _updatePlayer(dynamic state, dynamic sliderPosition) {
    if (state is int && sliderPosition is double) {
      try {
        _updateSlider(sliderPosition);
        final PlayerState newState = PlayerState.values[state];
        if (newState != _playerState) {
          switch (newState) {
            case PlayerState.PLAYING:
              _play();
              break;
            case PlayerState.PAUSED:
              _pause();
              break;
            case PlayerState.STOPPED:
            case PlayerState.COMPLETED:
              _stop();
              break;
          }
          _playerState = newState;
        }
      } catch (e) {
        if (kDebugMode) {
          print('sync player failed');
        }
      }
    }
  }

अब आप ऐप का परीक्षण करने के लिए तैयार हैं:

  1. कमांड लाइन पर, ऐप को एमुलेटर पर और/या ब्राउज़र में चलाएं: flutter run -d <device-name>
  2. किसी ब्राउज़र में, iOS सिम्युलेटर पर, या Android एमुलेटर पर ऐप्स खोलें। संदर्भ मेनू पर जाएं, लीडर डिवाइस बनने के लिए एक ऐप चुनें। आपको लीडर डिवाइस के अपडेट होने पर फ़ॉलोअर डिवाइस के प्लेयर्स को बदलते हुए देखने में सक्षम होना चाहिए।
  3. अब लीडर डिवाइस बदलें, संगीत चलाएं या रोकें, और फ़ॉलोअर डिवाइस को तदनुसार अपडेट होते हुए देखें।

यदि फॉलोअर डिवाइस ठीक से अपडेट हो जाते हैं, तो आप एक क्रॉस डिवाइस कंट्रोलर बनाने में सफल हो गए हैं। बस एक महत्वपूर्ण कदम बाकी है.

7. सुरक्षा नियम अपडेट करें

जब तक हम बेहतर सुरक्षा नियम नहीं लिखते, कोई व्यक्ति उस डिवाइस पर स्थिति लिख सकता है जो उसके पास नहीं है! इसलिए समाप्त करने से पहले, रीयलटाइम डेटाबेस सुरक्षा नियमों को अपडेट करें ताकि यह सुनिश्चित हो सके कि किसी डिवाइस पर केवल वही उपयोगकर्ता पढ़ या लिख ​​सकता है जो उस डिवाइस में साइन इन है। फ़ायरबेस कंसोल में, रीयलटाइम डेटाबेस पर जाएँ, और फिर नियम टैब पर जाएँ। केवल साइन इन किए गए उपयोगकर्ता को अपने डिवाइस की स्थिति पढ़ने और लिखने की अनुमति देने वाले निम्नलिखित नियम चिपकाएँ:

{
  "rules": {
    "users": {
           "$uid": {
               ".read": "$uid === auth.uid",
               ".write": "$uid === auth.uid"
           }
    },
  }
}

8. बधाई हो!

bcd986f7106d892b.gif

बधाई हो, आपने फ़्लटर का उपयोग करके सफलतापूर्वक एक क्रॉस डिवाइस रिमोट कंट्रोलर बनाया है!

क्रेडिट

बेटर टुगेदर, एक फायरबेस गाना

  • रयान वर्नोन द्वारा संगीत
  • मारिसा क्रिस्टी द्वारा गीत और एल्बम कवर
  • जेपी गोमेज़ द्वारा आवाज

9. बोनस

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

संदर्भ दस्तावेज़ और अगले चरण