1. Introdução
Última atualização: 14/03/2022
FlutterFire para comunicação entre dispositivos
Com o surgimento de um grande número de dispositivos de automação residencial, wearables e tecnologia de saúde pessoal on-line, a comunicação entre dispositivos se torna uma parte cada vez mais importante na criação de aplicativos para dispositivos móveis. Configurar a comunicação entre dispositivos, como controlar um navegador a partir de um aplicativo para celular ou controlar o que é reproduzido na TV a partir do telefone, é tradicionalmente mais complexo do que criar um aplicativo móvel normal .
o Realtime Database do Firebase fornece a API Presence , que permite aos usuários ver o status on-line/off-line dos dispositivos. você a usará com o serviço de instalações do Firebase para rastrear e conectar todos os dispositivos em que o mesmo usuário fez login. Você vai usar o Flutter para criar aplicativos rapidamente para várias plataformas e, em seguida, vai construir um protótipo para vários dispositivos que toca música em um dispositivo e controla a música em outro.
O que você vai criar
Neste codelab, você criará um controle remoto simples de player de música. Esse app vai:
- Crie um player de música simples no Android, iOS e Web com o Flutter.
- Permita que os usuários façam login.
- Conectar dispositivos quando o mesmo usuário estiver conectado em vários dispositivos.
- Permitir que os usuários controlem a reprodução de música em um dispositivo usando outro.
O que você aprenderá
- Como criar e executar um app de player de música do Flutter.
- Como permitir que os usuários façam login com o Firebase Auth.
- Como usar a API RTDB Presence do Firebase e o serviço de instalação do Firebase para conectar dispositivos.
O que é necessário
- Um ambiente de desenvolvimento do Flutter. Siga as instruções no guia de instalação do Flutter para fazer a configuração.
- É necessária uma versão mínima do Flutter 2.10 ou mais recente. Se você tiver uma versão anterior, execute
flutter upgrade.
- Uma conta do Firebase.
2. Etapas da configuração
Acessar o código inicial
Criamos um app de player de música no Flutter. O código inicial está localizado em um repositório Git. Para começar, na linha de comando, clone o repositório, acesse a pasta com o estado inicial e instale as dependências:
git clone https://github.com/FirebaseExtended/cross-device-controller.git
cd cross-device-controller/starter_code
flutter pub get
Criar o app
Você pode trabalhar com seu ambiente de desenvolvimento integrado favorito para criar o app ou usar a linha de comando.
No diretório do app, crie o app para Web com o comando flutter run -d web-server.
. O prompt a seguir vai aparecer.
lib/main.dart is being served at http://localhost:<port>
Acesse http://localhost:<port>
para conferir o player de música.
Se você já conhece o Android Emulator ou o simulador de iOS, pode criar o app para essas plataformas e instalá-lo com o comando flutter run -d <device_name>
.
O app da Web deve mostrar um player de música independente básico. Confira se os recursos do player estão funcionando como esperado. Este é um app simples de player de música projetado para este codelab. Ele só pode tocar uma música do Firebase, Better Together.
Configurar um emulador do Android ou um simulador do iOS
Se você já tiver um dispositivo Android ou iOS para desenvolvimento, pule esta etapa.
Para criar um emulador do Android, faça o download do Android Studio, que também oferece suporte ao desenvolvimento do Flutter, e siga as instruções em Criar e gerenciar dispositivos virtuais.
Para criar um simulador do iOS, você vai precisar de um ambiente Mac. Faça o download do XCode e siga as instruções em Visão geral do simulador > Usar o simulador > Abrir e fechar um simulador.
3. Como configurar o Firebase
Criar um projeto do Firebase
Abra um navegador para http://console.firebase.google.com/.
- Faça login no Firebase.
- No Console do Firebase, clique em Adicionar projeto ou Criar um projeto e nomeie seu projeto como Firebase-Cross-Device-Codelab.
- Clique nas opções de criação do projeto. Se for solicitado, aceite os termos do Firebase. Ignore a configuração do Google Analytics, porque você não vai usá-lo para este app.
Não é necessário fazer o download dos arquivos mencionados ou mudar os arquivos build.gradle. Elas serão configuradas ao inicializar o FlutterFire.
Instalar o SDK do Firebase
De volta na linha de comando, no diretório do projeto, execute o seguinte comando para instalar o Firebase:
flutter pub add firebase_core
No arquivo pubspec.yaml
, edite a versão do firebase_core
para pelo menos 1.13.1 ou execute flutter upgrade
.
Inicializar o FlutterFire
- Se você não tiver a CLI do Firebase instalada, execute
curl -sL https://firebase.tools | bash
para instalá-la. - Faça login executando
firebase login
e seguindo as instruções. - Instale a CLI do FlutterFire executando
dart pub global activate flutterfire_cli
. - Configure a CLI do FlutterFire executando
flutterfire configure
. - No prompt, escolha o projeto que você acabou de criar para este codelab, algo como Firebase-Cross-Device-Codelab.
- Selecione iOS, Android e Web quando for solicitado que você escolha o suporte à configuração.
- Quando o ID do pacote da Apple for solicitado, digite um domínio exclusivo ou
com.example.appname
, o que não é um problema para este codelab.
Depois de configurado, um arquivo firebase_options.dart
será gerado com todas as opções necessárias para a inicialização.
No editor, adicione o seguinte código ao arquivo main.dart para inicializar o Flutter e o 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());
}
Compile o app com o comando:
flutter run
Como você ainda não mudou nenhum elemento da interface, a aparência e o comportamento do app não mudaram. Mas agora você tem um app do Firebase e pode começar a usar os produtos do Firebase, incluindo:
- Firebase Authentication, que permite que os usuários façam login no app.
- Firebase Realtime Database(RTDB) você vai usar a API de presença para rastrear o status on-line/off-line do dispositivo
- As regras de segurança do Firebase vão permitir que você proteja o banco de dados.
- O serviço de instalações do Firebase para identificar os dispositivos em que um único usuário fez login.
4. Adicionar o Firebase Auth
Ativar o login por e-mail para o Firebase Authentication
Para permitir que os usuários façam login no app da Web, use o método de login E-mail/senha:
- No Console do Firebase, abra o menu Build no painel à esquerda.
- Clique em Autenticação e no botão Começar, depois na guia Método de login.
- Clique em E-mail/senha na lista Provedores de login, ative a chave Ativar e clique em Salvar.
Configurar o Firebase Authentication no Flutter
Na linha de comando, execute os comandos abaixo para instalar os pacotes do Flutter necessários:
flutter pub add firebase_auth
flutter pub add provider
Com essa configuração, agora é possível criar o fluxo de login e logout. Como o estado de autenticação não muda de uma tela para a outra, crie uma classe application_state.dart
para acompanhar as mudanças de estado no nível do app, como fazer login e sair. Saiba mais sobre isso na documentação Gerenciamento de estado do Flutter.
Cole o seguinte no novo arquivo 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();
}
}
Para garantir que o ApplicationState
seja inicializado quando o app for iniciado, adicione uma etapa de inicialização a 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(),
));
}
A interface do aplicativo deve permanecer a mesma, mas agora você pode permitir que os usuários façam login e salvem os estados do app.
Criar um fluxo de login
Nesta etapa, você trabalhará no fluxo de login e logout. Confira como o fluxo vai ficar:
- Um usuário desconectado iniciará o fluxo de login clicando no menu de contexto no lado direito da barra de apps.
- O fluxo de login será mostrado em uma caixa de diálogo.
- Se o usuário nunca tiver feito login antes, ele receberá uma solicitação para criar uma conta usando um endereço de e-mail válido e uma senha.
- Se o usuário já tiver feito login antes, ele receberá uma solicitação para digitar a senha.
- Depois que o usuário fizer login, clicar no menu de contexto vai mostrar a opção Sair.
A adição do fluxo de login requer três etapas.
Primeiro, crie um widget AppBarMenuButton
. Esse widget vai controlar o menu de contexto pop-up, dependendo do loginState
do usuário. Adicionar as importações
lib/src/widgets.dart
import 'application_state.dart';
import 'package:provider/provider.dart';
import 'authentication.dart';
Anexe o seguinte código a 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),
),
),
];
}
}
Depois, na mesma classe widgets.dart
, crie o widget 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,
),
),
]),
);
}
}
Depois, encontre o widget da barra de apps em main.dart.
. Adicione o AppBarMenuButton
para mostrar a opção Fazer login ou Sair.
lib/main.dart
import 'src/widgets.dart';
appBar: AppBar(
title: const Text('Music Box'),
backgroundColor: Colors.deepPurple.shade400,
actions: const <Widget>[
AppBarMenuButton(),
],
),
Execute o comando flutter run
para reiniciar o app com essas mudanças. O menu de contexto vai aparecer no lado direito da barra de apps. Ao clicar nele, você vai acessar uma caixa de diálogo de login.
Depois de fazer login com um endereço de e-mail e uma senha válidos, você vai encontrar a opção Sair no menu de contexto.
No console do Firebase, em Autenticação, você vai encontrar o endereço de e-mail listado como um novo usuário.
Parabéns! Os usuários já podem fazer login no app.
5. Adicionar conexão de banco de dados
Agora você já pode prosseguir para o registro de dispositivos usando a API Firebase Presence.
Na linha de comando, execute os comandos abaixo para adicionar as dependências necessárias:
flutter pub add firebase_app_installations
flutter pub add firebase_database
Criar um banco de dados
No console do Firebase,
- Navegue até a seção Realtime Database do Console do Firebase. Clique em Criar banco de dados.
- Se você precisar selecionar um modo inicial para suas regras de segurança, escolha Modo de teste por enquanto**.** O modo de teste cria regras de segurança que permitem a passagem de todas as solicitações. Você vai adicionar as regras de segurança mais tarde. É importante nunca ir para a produção com as regras de segurança ainda no modo de teste.
O banco de dados está vazio por enquanto. Localize o databaseURL
em Configurações do projeto, na guia Geral. Role para baixo até a seção Apps da Web.
Adicione o databaseURL
ao arquivo firebase_options.dart
:
lib/firebase_options.dart
static const FirebaseOptions web = FirebaseOptions(
apiKey: yourApiKey,
...
databaseURL: 'https://<YOUR_DATABASE_URL>,
...
);
Registrar dispositivos usando a API RTDB Presence
Você quer registrar os dispositivos de um usuário quando eles aparecem on-line. Para isso, use as instalações do Firebase e a API RTDB Presence do Firebase para monitorar uma lista de dispositivos on-line de um único usuário. O código a seguir vai ajudar a alcançar esse objetivo:
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';
}
Na linha de comando, crie e execute o app no dispositivo ou em um navegador com flutter run.
No app, faça login como um usuário. Não se esqueça de fazer login como o mesmo usuário em plataformas diferentes.
No Console do Firebase, seus dispositivos vão aparecer com um ID de usuário no banco de dados.
6. Sincronizar o estado do dispositivo
Selecionar um dispositivo principal
Para sincronizar estados entre dispositivos, designe um dispositivo como líder, ou o controlador. O dispositivo principal vai determinar os estados nos dispositivos secundários.
Crie um método setLeadDevice
no application_state.dart
e rastreie esse dispositivo com a chave active_device
no 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;
});
}
}
Para adicionar essa funcionalidade ao menu de contexto da barra de apps, crie uma PopupMenuItem
chamada Controller
modificando o widget SignedInMenuButton
. Esse menu permite que os usuários definam o dispositivo principal.
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();
}
}
}
Gravar o estado do dispositivo principal no banco de dados
Depois de definir um dispositivo principal, você pode sincronizar os estados dele com o RTDB usando o código abaixo. Anexe o código abaixo ao final de application_state.dart.
. Isso vai começar a armazenar dois atributos: o estado do player (reprodução ou pausa) e a posição do controle deslizante.
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');
}
}
}
Por fim, é necessário chamar setActiveDeviceState
sempre que o estado do player do controle for atualizado. Faça as seguintes alterações no arquivo 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;
}
Ler o estado do dispositivo principal do banco de dados
Há duas partes para ler e usar o estado do dispositivo principal. Primeiro, configure um listener de banco de dados para o estado do player principal em application_state
. Esse listener informará aos dispositivos seguidores quando atualizar a tela por meio de um callback. Você definiu uma interface OnLeadDeviceChangeCallback
nesta etapa. Ela ainda não está implementada. Você vai implementar essa interface em player_widget.dart
na próxima etapa.
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();
});
}
}
Em seguida, inicie o listener do banco de dados durante a inicialização do player em player_widget.dart
. Transmita a função _updatePlayer
para que o estado do player seguidor possa ser atualizado sempre que o valor do banco de dados mudar.
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');
}
}
}
}
Agora você já pode testar o app:
- Na linha de comando, execute o app em emuladores e/ou em um navegador com:
flutter run -d <device-name>
- Abra os apps em um navegador, em um simulador do iOS ou em um emulador do Android. Acesse o menu de contexto e escolha um app para ser o dispositivo líder. Você vai notar que os players dos dispositivos seguidores mudam conforme o dispositivo líder é atualizado.
- Agora mude o dispositivo líder, toque ou pause a música e observe os dispositivos seguidores sendo atualizados.
Se os dispositivos seguidores forem atualizados corretamente, você terá criado um controlador entre dispositivos. Só falta uma etapa importante.
7. Atualizar regras de segurança
A menos que criemos regras de segurança melhores, alguém pode gravar um estado em um dispositivo que não é dele. Portanto, antes de terminar, atualize as regras de segurança do Realtime Database para garantir que os únicos usuários que podem ler ou gravar em um dispositivo sejam os que estão conectados a ele. No Console do Firebase, navegue até o Realtime Database e, em seguida, até a guia Regras. Cole as seguintes regras que permitem que apenas usuários conectados leiam e gravem os próprios estados do dispositivo:
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
},
}
}
8. Parabéns!
Parabéns! Você criou um controle remoto para vários dispositivos usando o Flutter.
Créditos
Better Together, uma música do Firebase
- Música de Ryan Vernon
- Letra e capa do álbum por Marissa Christy
- Voz de JP Gomez
9. Bônus
Como um desafio extra, considere usar o Flutter FutureBuilder
para adicionar o tipo de dispositivo de lead atual à interface de forma assíncrona. Se você precisar de ajuda, ela será implementada na pasta que contém o estado concluído do codelab.