1. הקדמה
עדכון אחרון: 2022-03-14
FlutterFire לתקשורת בין מכשירים
ככל שאנו עדים למספר רב של מכשירים לבישים וטכנולוגיות בריאות אישיות באוטומציה ביתית המגיעים לאינטרנט, תקשורת חוצת מכשירים הופכת לחלק חשוב יותר ויותר בבניית יישומים ניידים. הגדרת תקשורת חוצת מכשירים כגון שליטה בדפדפן מאפליקציית טלפון, או שליטה במה שמנגן בטלוויזיה מהטלפון שלך, היא באופן מסורתי מורכבת יותר מבניית אפליקציה רגילה לנייד.
מסד הנתונים בזמן אמת של Firebase מספק את Presence API המאפשר למשתמשים לראות את מצב המכשיר שלהם מקוון/לא מקוון; תשתמש בו עם שירות התקנות Firebase כדי לעקוב ולחבר את כל המכשירים שבהם אותו משתמש נכנס. תשתמש ב-Flutter כדי ליצור במהירות יישומים עבור פלטפורמות מרובות, ולאחר מכן תבנה אב טיפוס חוצה מכשירים שמתנגן מוזיקה במכשיר אחד ושולטת במוזיקה במכשיר אחר!
מה שתבנה
בקוד מעבד זה, תבנה שלט רחוק פשוט של נגן מוזיקה. האפליקציה שלך תעשה:
- קבל נגן מוזיקה פשוט באנדרואיד, iOS ואינטרנט, שנבנה עם Flutter.
- אפשר למשתמשים להיכנס.
- חבר מכשירים כאשר אותו משתמש מחובר במספר מכשירים.
- אפשר למשתמשים לשלוט בהשמעת מוזיקה במכשיר אחד ממכשיר אחר.
מה תלמד
- כיצד לבנות ולהפעיל אפליקציית נגן מוזיקה של Flutter.
- כיצד לאפשר למשתמשים להיכנס באמצעות Firebase Auth.
- כיצד להשתמש ב-Firebase RTDB Presence API ובשירות ההתקנה של Firebase כדי לחבר התקנים.
מה אתה צריך
- סביבת פיתוח של Flutter. עקוב אחר ההוראות במדריך ההתקנה של Flutter כדי להגדיר אותו.
- נדרשת גרסת Flutter מינימלית של 2.10 ומעלה. אם יש לך גרסה נמוכה יותר, הפעל
flutter upgrade.
- חשבון Firebase.
2. מתחילים להתקין
קבל את קוד ההתחלה
יצרנו אפליקציית נגן מוזיקה בפלוטר. קוד ההתחלה נמצא בריפו של Git. כדי להתחיל, בשורת הפקודה, שכפל את ה-repo, עבור לתיקיה עם מצב ההתחלה, והתקן תלות:
git clone https://github.com/FirebaseExtended/cross-device-controller.git
cd cross-device-controller/starter_code
flutter pub get
בנה את האפליקציה
אתה יכול לעבוד עם ה-IDE המועדף עליך כדי לבנות את האפליקציה, או להשתמש בשורת הפקודה.
בספריית האפליקציה שלך, בנה את האפליקציה לאינטרנט עם הפקודה flutter run -d web-server.
אתה אמור להיות מסוגל לראות את ההנחיה הבאה.
lib/main.dart is being served at http://localhost:<port>
בקר בכתובת http://localhost:<port>
כדי לראות את נגן המוזיקה.
אם אתה מכיר את אמולטור אנדרואיד או סימולטור iOS, אתה יכול לבנות את האפליקציה עבור אותן פלטפורמות ולהתקין אותה עם הפקודה flutter run -d <device_name>
.
אפליקציית האינטרנט אמורה להציג נגן מוזיקה עצמאי בסיסי. ודא שתכונות הנגן פועלות כמתוכנן. זוהי אפליקציית נגן מוזיקה פשוטה המיועדת עבור מעבדת קוד זה. זה יכול להשמיע רק שיר של Firebase, Better Together .
הגדר אמולטור אנדרואיד או סימולטור iOS
אם כבר יש לך מכשיר אנדרואיד או מכשיר iOS לפיתוח, תוכל לדלג על שלב זה.
כדי ליצור אמולטור אנדרואיד, הורד את Android Studio שתומך גם בפיתוח Flutter, ופעל לפי ההוראות ביצירה וניהול מכשירים וירטואליים .
כדי ליצור סימולטור iOS, תזדקק לסביבת Mac. הורד את XCode , ובצע את ההוראות בסקירת הסימולטור > השתמש בסימולטור > פתח וסגור סימולטור .
3. הגדרת Firebase
צור פרויקט Firebase
פתח דפדפן אל http://console.firebase.google.com/ .
- היכנס ל- Firebase .
- במסוף Firebase, לחץ על הוסף פרויקט (או צור פרויקט ), ושם לפרויקט Firebase שלך Firebase-Cross-Device-Codelab .
- לחץ על אפשרויות יצירת הפרויקט. קבל את תנאי Firebase אם תתבקש. דלג על הגדרת Google Analytics, כי לא תשתמש ב-Analytics עבור האפליקציה הזו.
אין צורך להוריד את הקבצים שהוזכרו או לשנות קבצי build.gradle. אתה תגדיר אותם כאשר אתה מאתחל את FlutterFire.
התקן Firebase SDK
בחזרה בשורת הפקודה, בספריית הפרויקט, הפעל את הפקודה הבאה כדי להתקין את Firebase:
flutter pub add firebase_core
בקובץ pubspec.yaml
, ערוך את הגרסה עבור firebase_core
לגרסה 1.13.1 לפחות, או הפעל את flutter upgrade
אתחול FlutterFire
- אם לא התקנת את Firebase CLI, תוכל להתקין אותו על ידי הפעלת
curl -sL https://firebase.tools | bash
. - היכנס על ידי הפעלת
firebase login
וביצוע ההנחיות. - התקן את FlutterFire CLI על ידי הפעלת
dart pub global activate flutterfire_cli
. - הגדר את FlutterFire CLI על-ידי הפעלת
flutterfire configure
. - בהנחיה, בחר את הפרויקט שיצרת זה עתה עבור מעבד הקוד הזה, משהו כמו Firebase-Cross-Device-Codelab .
- בחר iOS , Android ו- Web כאשר תתבקש לבחור תמיכה בתצורה.
- כשתתבקש להזין את מזהה החבילה של Apple , הקלד דומיין ייחודי, או הזן
com.example.appname
, וזה בסדר עבור מעבדת הקוד הזה.
לאחר ההגדרה, ייווצר עבורך קובץ firebase_options.dart
המכיל את כל האפשרויות הנדרשות לאתחול.
בעורך שלך, הוסף את הקוד הבא לקובץ main.dart שלך כדי לאתחל את Flutter ו-Firebase:
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
עדיין לא שינית רכיבי ממשק משתמש, כך שהמראה וההתנהגות של האפליקציה לא השתנו. אבל עכשיו יש לך אפליקציית Firebase, ואתה יכול להתחיל להשתמש במוצרי Firebase, כולל:
- אימות Firebase , המאפשר למשתמשים שלך להיכנס לאפליקציה שלך.
- Firebase Realtime Database (RTDB) ; תשתמש בממשק API של נוכחות כדי לעקוב אחר מצב המכשיר המקוון/לא מקוון
- כללי האבטחה של Firebase יאפשרו לך לאבטח את מסד הנתונים.
- שירות התקנות Firebase כדי לזהות את המכשירים שמשתמש בודד נכנס אליהם.
4. הוסף Firebase Auth
אפשר כניסה בדוא"ל עבור אימות Firebase
כדי לאפשר למשתמשים להיכנס לאפליקציית האינטרנט, תשתמש בשיטת הכניסה לדוא"ל/סיסמה :
- במסוף Firebase, הרחב את תפריט Build בחלונית השמאלית.
- לחץ על אימות ולאחר מכן לחץ על הלחצן התחל , ולאחר מכן על הכרטיסייה שיטת כניסה .
- לחץ על דואר אלקטרוני/סיסמה ברשימת ספקי הכניסה , הגדר את המתג הפעל למצב מופעל ולאחר מכן לחץ על שמור .
הגדר את אימות Firebase ב-Flutter
בשורת הפקודה, הפעל את הפקודות הבאות כדי להתקין את חבילות הפלטר הדרושות:
flutter pub add firebase_auth
flutter pub add provider
עם תצורה זו, כעת תוכל ליצור את זרימת הכניסה והיציאה. מכיוון שמצב האימות לא אמור להשתנות ממסך למסך, תיצור מחלקה application_state.dart
כדי לעקוב אחר שינויים במצב רמת האפליקציה, כגון כניסה והתנתק. למידע נוסף על כך בתיעוד ניהול מצב Flutter .
הדבק את הדברים הבאים בקובץ 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(),
));
}
שוב, ממשק המשתמש של האפליקציה היה צריך להישאר זהה, אבל עכשיו אתה יכול לאפשר למשתמשים להיכנס ולשמור מצבי אפליקציה.
צור זרימת כניסה
בשלב זה, תעבדו על זרימת הכניסה והיציאה. כך תיראה הזרימה:
- משתמש מנותק יתחיל את זרימת הכניסה על ידי לחיצה על תפריט ההקשר בצד ימין של סרגל האפליקציות.
- זרימת הכניסה תוצג בתיבת דו-שיח.
- אם המשתמש מעולם לא נכנס לפני כן, הוא יתבקש ליצור חשבון באמצעות כתובת דוא"ל חוקית וסיסמה.
- אם המשתמש נכנס בעבר, הוא יתבקש להזין את הסיסמה שלו.
- לאחר שהמשתמש נכנס, לחיצה על תפריט ההקשר תציג אפשרות יציאה .
הוספת זרימת הכניסה דורשת שלושה שלבים.
קודם כל, צור ווידג'ט של 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,
),
),
]),
);
}
}
שלישית, מצא את הווידג'ט הקיים של appBar ב- 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
כדי להפעיל מחדש את האפליקציה עם השינויים האלה. אתה אמור להיות מסוגל לראות את תפריט ההקשר בצד ימין של סרגל האפליקציות. לחיצה עליו תעביר אותך לתיבת דו-שיח כניסה.
לאחר שתיכנס עם כתובת דוא"ל חוקית וסיסמה, אתה אמור להיות מסוגל לראות אפשרות יציאה בתפריט ההקשר.
במסוף Firebase, תחת אימות , אתה אמור להיות מסוגל לראות את כתובת הדוא"ל הרשומה כמשתמש חדש.
מזל טוב! משתמשים יכולים כעת להיכנס לאפליקציה!
5. הוסף חיבור למסד נתונים
כעת אתה מוכן לעבור לרישום מכשירים באמצעות ממשק ה-API של Firebase Presence.
בשורת הפקודה, הפעל את הפקודות הבאות כדי להוסיף את התלות הדרושות:
flutter pub add firebase_app_installations
flutter pub add firebase_database
צור מסד נתונים
במסוף Firebase,
- נווט לקטע מסד נתונים בזמן אמת של מסוף Firebase . לחץ על צור מסד נתונים .
- אם תתבקש לבחור מצב התחלה עבור כללי האבטחה שלך, בחר מצב בדיקה לעת עתה**.** (מצב בדיקה יוצר כללי אבטחה המאפשרים לעבור את כל הבקשות. אתה תוסיף כללי אבטחה מאוחר יותר. חשוב לעולם לא ללכת לייצור עם כללי האבטחה שלך עדיין במצב בדיקה.)
מסד הנתונים ריק לעת עתה. אתר את databaseURL
שלך בהגדרות פרוייקט , תחת הכרטיסייה כללי . גלול מטה למקטע יישומי אינטרנט .
הוסף את databaseURL
שלך לקובץ firebase_options.dart
:
lib/firebase_options.dart
static const FirebaseOptions web = FirebaseOptions(
apiKey: yourApiKey,
...
databaseURL: 'https://<YOUR_DATABASE_URL>,
...
);
רשום מכשירים באמצעות RTDB Presence API
אתה רוצה לרשום את המכשירים של משתמש כשהם מופיעים באינטרנט. כדי לעשות זאת, תנצל את התקנות Firebase ואת Firebase RTDB Presence API כדי לעקוב אחר רשימה של מכשירים מקוונים ממשתמש יחיד. הקוד הבא יעזור להשיג מטרה זו:
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.
באפליקציה שלך, היכנס כמשתמש. זכור להיכנס כאותו משתמש בפלטפורמות שונות.
במסוף Firebase , אתה אמור לראות את המכשירים שלך מופיעים תחת מזהה משתמש אחד במסד הנתונים שלך.
6. סנכרון מצב המכשיר
בחר התקן מוביל
כדי לסנכרן מצבים בין מכשירים, הגדר מכשיר אחד כמוביל, או הבקר. ההתקן המוביל יכתיב את המצבים במכשירי העוקבים.
צור שיטה setLeadDevice
ב- application_state.dart
, ועקוב אחר מכשיר זה עם המפתח active_device
ב-RTDB:
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;
});
}
}
כדי להוסיף פונקציונליות זו לתפריט ההקשר של שורת האפליקציה, צור PopupMenuItem
בשם Controller
על ידי שינוי הווידג'ט SignedInMenuButton
. תפריט זה יאפשר למשתמשים להגדיר את ההתקן המוביל.
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();
}
}
}
כתוב את מצב ההתקן המוביל למסד הנתונים
לאחר שהגדרתם התקן לידים, תוכלו לסנכרן את מצבי ההתקן המוביל ל-RTDB עם הקוד הבא. הוסף את הקוד הבא לסוף של 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');
}
}
}
}
עכשיו אתה מוכן לבדוק את האפליקציה:
- בשורת הפקודה, הפעל את האפליקציה על אמולטורים ו/או בדפדפן עם:
flutter run -d <device-name>
- פתח את האפליקציות בדפדפן, בסימולטור iOS או אמולטור אנדרואיד. עבור לתפריט ההקשר, בחר אפליקציה אחת שתהיה התקן המוביל. אתה אמור להיות מסוגל לראות את השחקנים של מכשירי העוקבים משתנים ככל שהמכשיר המוביל מתעדכן.
- כעת שנה את מכשיר המוביל, הפעל או השהה מוזיקה, וצפה במכשירי העוקבים המתעדכנים בהתאם.
אם מכשירי העוקבים מתעדכנים כראוי, הצלחת ליצור בקר חוצה מכשירים. נותר רק צעד אחד מכריע.
7. עדכן את כללי האבטחה
אלא אם כן נכתוב כללי אבטחה טובים יותר, מישהו יכול לכתוב מצב למכשיר שאינו בבעלותו! אז לפני שתסיים, עדכן את כללי האבטחה של מסד נתונים בזמן אמת כדי לוודא שהמשתמשים היחידים שיכולים לקרוא או לכתוב למכשיר הוא המשתמש שנכנס לאותו מכשיר. במסוף Firebase, נווט אל מסד הנתונים בזמן אמת ולאחר מכן אל הכרטיסייה כללים . הדבק את הכללים הבאים המאפשרים רק למשתמש מחובר לקרוא ולכתוב את מצבי המכשיר שלו:
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
},
}
}
8. מזל טוב!
מזל טוב, בנית בהצלחה שלט רחוק בין מכשירים באמצעות Flutter!
נקודות זכות
Better Together, שיר של Firebase
- מוזיקה מאת ריאן ורנון
- מילים ועטיפה של האלבום מאת Marissa Christy
- קול מאת JP Gomez
9. בונוס
כאתגר נוסף, שקול להשתמש ב-Flutter FutureBuilder
כדי להוסיף את סוג התקן הלידים הנוכחי לממשק המשתמש באופן אסינכרוני. אם אתה צריך סיוע, הוא מיושם בתיקייה המכילה את המצב המוגמר של מעבדת הקוד.