1. مقدمه
آخرین به روز رسانی: 2022-03-14
FlutterFire برای ارتباط متقابل دستگاه
از آنجایی که شاهد حضور تعداد زیادی از دستگاههای اتوماسیون خانگی، پوشیدنی و فناوری سلامت شخصی هستیم، ارتباطات بین دستگاهی به بخش مهمی از ساخت برنامههای تلفن همراه تبدیل میشود. راهاندازی ارتباطات متقابل دستگاهی مانند کنترل مرورگر از طریق برنامه تلفن یا کنترل آنچه در تلویزیون شما از طریق تلفن شما پخش میشود، به طور سنتی پیچیدهتر از ساخت یک برنامه تلفن همراه معمولی است.
پایگاه داده بیدرنگ Firebase یک API حضوری را ارائه می دهد که به کاربران امکان می دهد وضعیت دستگاه خود را آنلاین/آفلاین ببینند. شما از آن با سرویس نصب Firebase برای ردیابی و اتصال همه دستگاههایی که همان کاربر به سیستم وارد شده است استفاده خواهید کرد. از Flutter برای ایجاد سریع برنامهها برای پلتفرمهای مختلف استفاده میکنید و سپس یک نمونه اولیه از دستگاههای متقاطع میسازید که پخش میشود. موسیقی در یک دستگاه و کنترل موسیقی در دستگاه دیگر!
چیزی که خواهی ساخت
در این کد لبه، شما یک کنترل از راه دور پخش کننده موسیقی ساده خواهید ساخت. برنامه شما:
- یک پخش کننده موسیقی ساده در Android، iOS و وب داشته باشید که با Flutter ساخته شده است.
- به کاربران اجازه ورود به سیستم را بدهید.
- وقتی همان کاربر در چندین دستگاه وارد سیستم شده است، دستگاهها را متصل کنید.
- به کاربران امکان کنترل پخش موسیقی در یک دستگاه از دستگاه دیگر را بدهید.
چیزی که یاد خواهید گرفت
- نحوه ساخت و اجرای برنامه پخش کننده موسیقی Flutter.
- چگونه به کاربران اجازه دهیم با Firebase Auth وارد شوند.
- نحوه استفاده از Firebase RTDB Presence API و Firebase Installation Service برای اتصال دستگاه ها.
آنچه شما نیاز دارید
- یک محیط توسعه فلاتر. دستورالعمل های راهنمای نصب Flutter را برای تنظیم آن دنبال کنید.
- حداقل نسخه فلاتر 2.10 یا بالاتر مورد نیاز است. اگر نسخه پایین تری دارید،
flutter upgrade.
- یک حساب Firebase
2. راه اندازی
کد شروع را دریافت کنید
ما یک برنامه پخش موسیقی در Flutter ایجاد کرده ایم. کد شروع در یک مخزن Git قرار دارد. برای شروع، در خط فرمان، مخزن را شبیه سازی کنید، به پوشه ای با حالت شروع حرکت کنید و وابستگی ها را نصب کنید:
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 نیز پشتیبانی میکند و دستورالعملهای موجود در Create and management devices مجازی را دنبال کنید.
برای ایجاد یک شبیه ساز iOS، به یک محیط مک نیاز دارید. XCode را دانلود کنید و دستورالعملهای موجود در Simulator Overview > Use Simulator > باز کردن و بستن یک شبیهساز را دنبال کنید.
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 configure
FlutterFire CLI را پیکربندی کنید. - در اعلان، پروژه ای را که به تازگی برای این Codelab ایجاد کرده اید، انتخاب کنید، چیزی مانند Firebase-Cross-Device-Codelab .
- وقتی از شما خواسته شد که پشتیبانی پیکربندی را انتخاب کنید، iOS ، Android و Web را انتخاب کنید.
- وقتی از اپل باندل ID خواسته شد، یک دامنه منحصر به فرد را تایپ کنید یا
com.example.appname
وارد کنید، که برای اهداف این Codelab مناسب است.
پس از پیکربندی، یک فایل 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 (RTDB) ; شما از API حضور برای ردیابی وضعیت آنلاین/آفلاین دستگاه استفاده خواهید کرد
- قوانین امنیتی Firebase به شما این امکان را می دهد که پایگاه داده را ایمن کنید.
- Firebase Installations Service برای شناسایی دستگاههایی که یک کاربر به سیستم وارد شده است.
4. Firebase Auth را اضافه کنید
ورود به ایمیل را برای احراز هویت Firebase فعال کنید
برای اینکه به کاربران اجازه دهید وارد برنامه وب شوند، از روش ورود به سیستم ایمیل/گذرواژه استفاده خواهید کرد:
- در کنسول Firebase، منوی Build را در پانل سمت چپ گسترش دهید.
- روی Authentication کلیک کنید و سپس روی دکمه شروع و سپس برگه روش ورود به سیستم کلیک کنید.
- روی ایمیل/گذرواژه در لیست ارائه دهندگان ورود به سیستم کلیک کنید، سوئیچ Enable را روی موقعیت روشن قرار دهید و سپس روی ذخیره کلیک کنید.
احراز هویت 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(),
));
}
باز هم، رابط کاربری برنامه باید ثابت می ماند، اما اکنون می توانید به کاربران اجازه دهید وارد سیستم شوند و وضعیت های برنامه را ذخیره کنند.
یک علامت در جریان ایجاد کنید
در این مرحله شما روی جریان ورود و خروج کار خواهید کرد. در اینجا جریان به چه صورت خواهد بود:
- کاربر خارج شده با کلیک بر روی منوی زمینه، جریان ورود به سیستم را آغاز می کند در سمت راست نوار برنامه
- جریان ورود به سیستم در یک گفتگو نمایش داده می شود.
- اگر کاربر قبلاً هرگز وارد سیستم نشده باشد، از او خواسته می شود با استفاده از یک آدرس ایمیل معتبر و یک رمز عبور، یک حساب کاربری ایجاد کند.
- اگر کاربر قبلا وارد سیستم شده باشد، از او خواسته می شود رمز عبور خود را وارد کند.
- پس از ورود کاربر، با کلیک بر روی منوی زمینه، گزینه Sign out نمایش داده می شود.
افزودن جریان ورود به سیستم به سه مرحله نیاز دارد.
اول از همه، یک ویجت 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
را اضافه کنید تا گزینه Sign in یا Sign out نمایش داده شود.
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، در بخش Authentication ، باید بتوانید آدرس ایمیل فهرست شده به عنوان کاربر جدید را ببینید.
تبریک می گویم! کاربران اکنون می توانند وارد برنامه شوند!
5. اتصال پایگاه داده را اضافه کنید
اکنون آماده هستید تا با استفاده از Firebase Presence API به ثبت نام دستگاه بروید.
در خط فرمان، دستورات زیر را اجرا کنید تا وابستگی های لازم را اضافه کنید:
flutter pub add firebase_app_installations
flutter pub add firebase_database
یک پایگاه داده ایجاد کنید
در کنسول Firebase،
- به بخش Realtime Database کنسول 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 Installations و 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;
});
}
}
برای افزودن این قابلیت به منوی زمینه نوار برنامه، با تغییر ویجت SignedInMenuButton
، یک PopupMenuItem
به نام Controller
ایجاد کنید. این منو به کاربران این امکان را می دهد که دستگاه اصلی را تنظیم کنند.
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، به پایگاه داده Realtime و سپس به تب Rules بروید. قوانین زیر را جایگذاری کنید تا فقط کاربرانی که به سیستم وارد شدهاند اجازه میدهند حالتهای دستگاه خود را بخوانند و بنویسند:
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
},
}
}
8. تبریک می گویم!
تبریک میگوییم، شما با موفقیت یک کنترلکننده از راه دور متقاطع با استفاده از Flutter ساختید!
اعتبارات
Better Together, a Firebase Song
- موسیقی از رایان ورنون
- شعر و جلد آلبوم از ماریسا کریستی
- صدای جی پی گومز
9. پاداش
به عنوان یک چالش اضافی، استفاده از Flutter FutureBuilder
را در نظر بگیرید تا نوع دستگاه اصلی فعلی را به طور ناهمزمان به رابط کاربری اضافه کنید. اگر به کمک نیاز دارید، آن را در پوشه ای که حاوی وضعیت تمام شده کد لبه است پیاده سازی می شود.