1. Giriş
Son Güncelleme: 2022-03-14
Cihazlar arası iletişim için FlutterFire
Ev otomasyonu, giyilebilir cihazlar ve kişisel sağlık teknolojisi cihazlarının sayısının artmasıyla birlikte cihazlar arası iletişim, mobil uygulamalar geliştirmenin giderek daha önemli bir parçası haline geliyor. Cihazlar arası iletişimi ayarlamak (ör. bir tarayıcıyı telefon uygulamasından kontrol etmek veya TV'nizde oynatılan içerikleri telefonunuzdan kontrol etmek) normal bir mobil uygulama oluşturmaktan daha karmaşıktır.
Firebase'in Realtime Database'i, kullanıcılara cihazlarının çevrimiçi/çevrimdışı durumunu görme olanağı tanıyan Presence API 'yi sağlar. Bu API'yi, aynı kullanıcının oturum açtığı tüm cihazları izlemek ve bağlamak için Firebase Installations Service ile birlikte kullanırsınız. Flutter'ı kullanarak birden fazla platform için hızlıca uygulamalar oluşturacak, ardından bir cihazda müzik çalan ve başka bir cihazda müziği kontrol eden cihazlar arası bir prototip oluşturacaksınız.
Ne oluşturacaksınız?
Bu codelab'de basit bir müzik çalar uzaktan kumandası oluşturacaksınız. Uygulamanız şunları yapabilecek:
- Flutter ile oluşturulmuş, Android, iOS ve web'de basit bir müzik çalar kullanın.
- Kullanıcıların oturum açmasına izin verin.
- Aynı kullanıcı birden fazla cihazda oturum açtığında cihazları bağlayın.
- Kullanıcıların bir cihazdaki müzik oynatmayı başka bir cihazdan kontrol etmesine izin verin.
Neler öğreneceksiniz?
- Flutter müzik çalar uygulaması oluşturma ve çalıştırma
- Kullanıcıların Firebase Auth ile oturum açmasına nasıl izin verileceği açıklanmaktadır.
- Cihazları bağlamak için Firebase RTDB Presence API ve Firebase Installation Service'i kullanma
Gerekenler
- Flutter geliştirme ortamı. Kurulum için Flutter yükleme kılavuzundaki talimatları uygulayın.
- Minimum Flutter sürümü 2.10 veya daha yeni olmalıdır. Daha eski bir sürüm kullanıyorsanız
flutter upgrade.
komutunu çalıştırın. - Firebase hesabı.
2. Hazırlanma
Başlangıç kodunu alma
Flutter'da bir müzik çalar uygulaması oluşturduk. Başlangıç kodu bir Git deposunda bulunur. Başlamak için komut satırında depoyu klonlayın, başlangıç durumunu içeren klasöre gidin ve bağımlılıkları yükleyin:
git clone https://github.com/FirebaseExtended/cross-device-controller.git
cd cross-device-controller/starter_code
flutter pub get
Uygulamayı oluşturma
Uygulamayı oluşturmak için en sevdiğiniz IDE'yi kullanabilir veya komut satırını tercih edebilirsiniz.
Uygulama dizininizde, flutter run -d web-server.
komutuyla web için uygulamayı oluşturun. Aşağıdaki istemi görmeniz gerekir.
lib/main.dart is being served at http://localhost:<port>
Müzik oynatıcıyı görmek için http://localhost:<port>
adresini ziyaret edin.
Android emülatörünü veya iOS simülatörünü kullanmayı biliyorsanız uygulamayı bu platformlar için oluşturabilir ve flutter run -d <device_name>
komutuyla yükleyebilirsiniz.
Web uygulaması, temel bir bağımsız müzik çalar göstermelidir. Oynatıcı özelliklerinin beklendiği gibi çalıştığından emin olun. Bu, bu codelab için tasarlanmış basit bir müzik çalar uygulamasıdır. Yalnızca Firebase şarkısı olan Better Together'ı çalabilir.
Android emülatörü veya iOS simülatörü ayarlama
Geliştirme için Android veya iOS cihazınız varsa bu adımı atlayabilirsiniz.
Android emülatörü oluşturmak için Flutter geliştirmeyi de destekleyen Android Studio'yu indirin ve Sanal cihaz oluşturma ve yönetme bölümündeki talimatları uygulayın.
iOS simülatörü oluşturmak için Mac ortamı gerekir. XCode'u indirin ve Simulator Overview > Use Simulator > Open and close a simulator (Simülatöre Genel Bakış > Simülatörü Kullanma > Simülatörü açma ve kapatma) bölümündeki talimatları uygulayın.
3. Firebase'i ayarlama
Firebase projesi oluşturma
- Google Hesabınızı kullanarak Firebase konsolunda oturum açın.
- Yeni bir proje oluşturmak için düğmeyi tıklayın ve ardından bir proje adı girin (örneğin,
Firebase-Cross-Device-Codelab
).
- Devam'ı tıklayın.
- İstenirse Firebase şartlarını inceleyip kabul edin ve Devam'ı tıklayın.
- (İsteğe bağlı) Firebase konsolunda yapay zeka yardımını etkinleştirin ("Firebase'de Gemini" olarak adlandırılır).
- Bu codelab için Google Analytics'e ihtiyacınız yoktur. Bu nedenle, Google Analytics seçeneğini devre dışı bırakın.
- Proje oluştur'u tıklayın, projenizin hazırlanmasını bekleyin ve ardından Devam'ı tıklayın.
Firebase SDK'sını yükleme
Komut satırına geri dönüp proje dizininde Firebase'i yüklemek için aşağıdaki komutu çalıştırın:
flutter pub add firebase_core
pubspec.yaml
dosyasında, firebase_core
için sürümü en az 1.13.1 olacak şekilde düzenleyin veya flutter upgrade
komutunu çalıştırın.
FlutterFire'ı başlatma
- Firebase CLI yüklü değilse
curl -sL https://firebase.tools | bash
komutunu çalıştırarak yükleyebilirsiniz. firebase login
komutunu çalıştırıp istemleri uygulayarak giriş yapın.dart pub global activate flutterfire_cli
komutunu çalıştırarak FlutterFire CLI'yı yükleyin.flutterfire configure
komutunu çalıştırarak FlutterFire CLI'yı yapılandırın.- İstemde, bu codelab için yeni oluşturduğunuz projeyi (ör. Firebase-Cross-Device-Codelab) seçin.
- Yapılandırma desteği seçmeniz istendiğinde iOS, Android ve Web'i seçin.
- Apple paket kimliği istendiğinde benzersiz bir alan adı yazın veya bu codelab'in amacı için uygun olan
com.example.appname
karakterini girin.
Yapılandırma tamamlandıktan sonra başlatma için gereken tüm seçenekleri içeren bir firebase_options.dart
dosyası oluşturulur.
Flutter ve Firebase'i başlatmak için düzenleyicinizde main.dart dosyanıza aşağıdaki kodu ekleyin:
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());
}
Uygulamayı şu komutla derleyin:
flutter run
Henüz herhangi bir kullanıcı arayüzü öğesini değiştirmediğiniz için uygulamanın görünümü ve davranışı değişmedi. Ancak artık bir Firebase uygulamanız var ve aşağıdakiler de dahil olmak üzere Firebase ürünlerini kullanmaya başlayabilirsiniz:
- Kullanıcılarınızın uygulamanızda oturum açmasına olanak tanıyan Firebase Authentication
- Firebase Realtime Database(RTDB): Cihazın çevrimiçi/çevrimdışı durumunu izlemek için varlık API'sini kullanırsınız.
- Firebase Güvenlik Kuralları, veritabanını güvenli hale getirmenize olanak tanır.
- Tek bir kullanıcının oturum açtığı cihazları tanımlamak için Firebase Installations Service.
4. Firebase Auth'u ekleme
Firebase Authentication için e-posta ile oturum açmayı etkinleştirme
Kullanıcıların web uygulamasında oturum açmasına izin vermek için E-posta/Şifre oturum açma yöntemini kullanırsınız:
- Firebase konsolunda, sol paneldeki Build (Oluştur) menüsünü genişletin.
- Kimlik doğrulama'yı, ardından Başlayın düğmesini ve Oturum açma yöntemi sekmesini tıklayın.
- Oturum açma sağlayıcıları listesinde E-posta/Şifre'yi tıklayın, Etkinleştir anahtarını açık konuma getirin ve Kaydet'i tıklayın.
Flutter'da Firebase Authentication'ı yapılandırma
Komut satırında, gerekli Flutter paketlerini yüklemek için aşağıdaki komutları çalıştırın:
flutter pub add firebase_auth
flutter pub add provider
Bu yapılandırmayla artık oturum açma ve kapatma akışını oluşturabilirsiniz. Kimlik doğrulama durumu ekrandan ekrana değişmemesi gerektiğinden, oturum açma ve kapatma gibi uygulama düzeyindeki durum değişikliklerini takip etmek için bir application_state.dart
sınıfı oluşturacaksınız. Bu konu hakkında daha fazla bilgiyi Flutter durum yönetimi belgelerinde bulabilirsiniz.
Aşağıdaki komutu yeni application_state.dart
dosyasına yapıştırın:
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();
}
}
Uygulama başlatıldığında ApplicationState
öğesinin başlatıldığından emin olmak için main.dart
öğesine bir başlatma adımı ekleyeceksiniz:
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(),
));
}
Uygulama kullanıcı arayüzü yine aynı kalmalıdır ancak artık kullanıcıların oturum açmasına ve uygulama durumlarını kaydetmesine izin verebilirsiniz.
Oturum açma akışı oluşturma
Bu adımda, oturum açma ve oturumu kapatma akışı üzerinde çalışacaksınız. Akışın görünümü aşağıdaki gibidir:
- Oturumu kapalı bir kullanıcı, uygulama çubuğunun sağ tarafındaki bağlam menüsünü
tıklayarak oturum açma akışını başlatır.
- Oturum açma akışı bir iletişim kutusunda gösterilir.
- Daha önce hiç oturum açmamış olan kullanıcılardan geçerli bir e-posta adresi ve şifre kullanarak hesap oluşturmaları istenir.
- Daha önce oturum açmış olan kullanıcılardan şifrelerini girmeleri istenir.
- Kullanıcı oturum açtıktan sonra bağlam menüsünü tıkladığında Oturumu kapat seçeneği gösterilir.
Oturum açma akışı eklemek için üç adım gerekir.
Öncelikle bir AppBarMenuButton
widget'ı oluşturun. Bu widget, kullanıcının loginState
özelliğine bağlı olarak içerik menüsü pop-up'ını kontrol eder. İçe aktarılanları ekleme
lib/src/widgets.dart
import 'application_state.dart';
import 'package:provider/provider.dart';
import 'authentication.dart';
Aşağıdaki kodu widgets.dart.
dosyasına ekleyin.
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),
),
),
];
}
}
İkinci olarak, aynı widgets.dart
sınıfında SignInDialog
widget'ını oluşturun.
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,
),
),
]),
);
}
}
Üçüncü olarak, main.dart.
AppBarMenuButton
öğesini ekleyerek Oturum aç veya Oturumu kapat seçeneğini görüntülemek için mevcut appBar widget'ını bulun.
lib/main.dart
import 'src/widgets.dart';
appBar: AppBar(
title: const Text('Music Box'),
backgroundColor: Colors.deepPurple.shade400,
actions: const <Widget>[
AppBarMenuButton(),
],
),
Uygulamayı bu değişikliklerle yeniden başlatmak için flutter run
komutunu çalıştırın. Uygulama çubuğunun sağ tarafında içerik menüsünü görebilirsiniz. Bu seçeneği tıkladığınızda oturum açma iletişim kutusuna yönlendirilirsiniz.
Geçerli bir e-posta adresi ve şifreyle giriş yaptıktan sonra içerik menüsünde Oturumu kapat seçeneğini görebilirsiniz.
Firebase konsolundaki Authentication (Kimlik Doğrulama) bölümünde, e-posta adresinin yeni kullanıcı olarak listelendiğini görebilirsiniz.
Tebrikler! Kullanıcılar artık uygulamada oturum açabilir.
5. Veritabanı bağlantısı ekleme
Artık Firebase Presence API'yi kullanarak cihaz kaydına geçebilirsiniz.
Gerekli bağımlılıkları eklemek için komut satırında aşağıdaki komutları çalıştırın:
flutter pub add firebase_app_installations
flutter pub add firebase_database
Veritabanı oluşturun
Firebase konsolunda:
- Firebase konsolunun Realtime Database bölümüne gidin. Veritabanı Oluştur'u tıklayın.
- Güvenlik kurallarınız için bir başlangıç modu seçmeniz istenirse şimdilik Test Modu'nu seçin**.** (Test modu, tüm isteklere izin veren güvenlik kuralları oluşturur. Güvenlik kurallarını daha sonra ekleyeceksiniz. Güvenlik kurallarınız hâlâ test modundayken hiçbir zaman üretime geçmemeniz önemlidir.)
Veritabanı şu anda boş. databaseURL
, Proje ayarları bölümündeki Genel sekmesinde bulunur. Web uygulamaları bölümüne ilerleyin.
databaseURL
dosyanızı firebase_options.dart
dosyasına ekleyin:
lib/firebase_options.dart
static const FirebaseOptions web = FirebaseOptions(
apiKey: yourApiKey,
...
databaseURL: 'https://<YOUR_DATABASE_URL>,
...
);
RTDB Presence API'yi kullanarak cihazları kaydetme
Kullanıcıların cihazları internete bağlandığında kaydedilmesini istiyorsanız. Bunu yapmak için tek bir kullanıcının çevrimiçi cihazlarının listesini takip etmek üzere Firebase Installations ve Firebase RTDB Presence API'den yararlanacaksınız. Bu hedefe ulaşmanıza yardımcı olacak kodu aşağıda bulabilirsiniz:
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';
}
Komut satırına geri dönün ve uygulamayı cihazınızda veya tarayıcıda flutter run.
ile oluşturup çalıştırın.
Uygulamanızda kullanıcı olarak oturum açın. Farklı platformlarda aynı kullanıcı olarak oturum açmayı unutmayın.
Firebase konsolunda, cihazlarınızın veritabanınızda tek bir kullanıcı kimliği altında gösterildiğini görmeniz gerekir.
6. Cihaz senkronizasyon durumu
Bir ana cihaz seçin
Durumları cihazlar arasında senkronize etmek için bir cihazı lider veya denetleyici olarak belirleyin. Ana cihaz, takipçi cihazlardaki durumları belirler.
application_state.dart
içinde bir setLeadDevice
yöntemi oluşturun ve bu cihazı RTDB'deki active_device
anahtarıyla takip edin:
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;
});
}
}
Bu işlevi uygulama çubuğu bağlam menüsüne eklemek için PopupMenuItem
adlı bir Controller
oluşturun. Bunu, SignedInMenuButton
widget'ını değiştirerek yapabilirsiniz. Bu menü, kullanıcıların birincil cihazı ayarlamasına olanak tanır.
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();
}
}
}
Birincil cihazın durumunu veritabanına yazma
Birincil cihazı ayarladıktan sonra aşağıdaki kodu kullanarak birincil cihazın durumlarını RTDB ile senkronize edebilirsiniz. Aşağıdaki kodu application_state.dart.
kodunun sonuna ekleyin. Bu işlem, iki özelliği (oynatıcı durumu: oynatma veya duraklatma ve kaydırma çubuğu konumu) depolamaya başlar.
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');
}
}
}
Son olarak, kontrol cihazının oynatıcı durumu her güncellendiğinde setActiveDeviceState
işlevini çağırmanız gerekir. Mevcut player_widget.dart
dosyasında aşağıdaki değişiklikleri yapın:
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;
}
Birincil cihazın durumunu veritabanından okuma
Birincil cihazın durumunu okumak ve kullanmak için iki bölüm vardır. Öncelikle, application_state
içinde ana oynatıcı durumunun veritabanı dinleyicisini ayarlamanız gerekir. Bu dinleyici, geri çağırma yoluyla takipçi cihazlara ekranı ne zaman güncelleyeceklerini bildirir. Bu adımda bir arayüz OnLeadDeviceChangeCallback
tanımladığınızı fark edin. Henüz uygulanmamıştır. Bu arayüzü bir sonraki adımda player_widget.dart
içinde uygulayacaksınız.
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();
});
}
}
İkinci olarak, player_widget.dart
içinde oynatıcı başlatma sırasında veritabanı dinleyicisini başlatın. _updatePlayer
işlevini iletin. Böylece, veritabanı değeri her değiştiğinde takipçi oyuncu durumu güncellenebilir.
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');
}
}
}
}
Artık uygulamayı test etmeye hazırsınız:
- Komut satırında, uygulamayı emülatörlerde ve/veya tarayıcıda şu komutla çalıştırın:
flutter run -d <device-name>
- Uygulamaları tarayıcıda, iOS simülatöründe veya Android emülatöründe açın. Bağlam menüsüne gidin ve bir uygulamayı lider cihaz olarak seçin. Lider cihaz güncellendikçe takipçi cihazların oynatıcılarının değiştiğini görebilirsiniz.
- Şimdi lider cihazı değiştirin, müzik çalın veya duraklatın ve takipçi cihazların buna göre güncellendiğini gözlemleyin.
Takipçi cihazlar düzgün şekilde güncellenirse cihazlar arası denetleyici oluşturma işlemini başarıyla tamamlamışsınızdır. Geriye yalnızca bir önemli adım kaldı.
7. Güvenlik kurallarını güncelleme
Daha iyi güvenlik kuralları yazmadığımız sürece, birisi kendisine ait olmayan bir cihaza durum yazabilir. Bu nedenle, bitirmeden önce Realtime Database güvenlik kurallarını güncelleyerek yalnızca ilgili cihazda oturum açmış kullanıcının cihazı okuyabildiğinden veya cihaza yazabildiğinden emin olun. Firebase konsolunda Realtime Database'e, ardından Kurallar sekmesine gidin. Yalnızca oturum açmış kullanıcının kendi cihaz durumlarını okumasına ve yazmasına izin veren aşağıdaki kuralları yapıştırın:
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
},
}
}
8. Tebrikler!
Tebrikler, Flutter kullanarak cihazlar arası uzaktan kumanda oluşturmayı başarıyla tamamladınız.
Kredi
Better Together, a Firebase Song
- Müzikleri Ryan Vernon'a ait yapımlar
- Sözler ve albüm kapağı: Marissa Christy
- Seslendiren: JP Gomez
9. Bonus
Ek bir zorluk olarak, mevcut ana cihaz türünü kullanıcı arayüzüne eşzamansız olarak eklemek için Flutter'ı FutureBuilder
kullanmayı deneyin. Yardıma ihtiyacınız olursa bu özellik, codelab'in tamamlanmış durumunu içeren klasörde uygulanır.