push_service_client
Cliente Flutter para o Push Service — plataforma de push notifications. O pacote trata de tudo: inicializa o Firebase, pede permissões, obtém o token FCM/APNs, captura sozinho os dados do device (modelo, versões de app/SO, idioma, fuso horário), regista-o no serviço e liga os listeners para receber notificações.
O developer não precisa de saber qual é o token correto nem de embutir segredos na app.
Só precisa do App ID público e de guardar o subscriptionId devolvido — é esse o id
que o backend dele usa para enviar pushes.
Funcionalidades
initialize()— faz tudo: Firebase + permissões + token + captura automática + registo + listeners- Captura automática: modelo, versão do SO, versão da app, idioma e fuso horário (IANA)
subscriptionId(UUID estável) — o identificador público para enviar pushes- Associar o device ao utilizador (
setExternalId/clearExternalId) — login/logout - Subscribe / Unsubscribe / trackActivity — endpoints públicos, sem token
- Envio de pushes — para
devices:concretos ou segmentos (requer REST key) - Re-registo automático quando o token FCM é atualizado
- Handlers de notificação — callbacks para push aberto/recebido
- Mensagens in-app — busca automática no arranque da sessão, gatilhos (abrir app, duração de sessão, desde a última mensagem, personalizados), frequência e métricas (impressões/cliques/dispensas)
Instalação
O pacote está publicado no pub.dev.
1. Adiciona o pacote
No pubspec.yaml do teu projeto:
dependencies:
push_service_client: ^1.1.0
Depois corre:
flutter pub get
2. Configura o Firebase
O pacote usa firebase_core e firebase_messaging (já incluídos como dependências).
- Android: adiciona o ficheiro
google-services.json(da consola Firebase) emandroid/app/e o plugincom.google.gms.google-servicesnoandroid/build.gradle. - iOS: adiciona o ficheiro
GoogleService-Info.plistemios/Runner/.
Consulta o guia oficial do Firebase Flutter para os passos detalhados.
Utilização
1. Configura
Obtém o App ID público da app no painel do Push Service
(Aplicações → API → App ID). É um UUID não secreto — pode ir dentro da app.
O servidor (https://push.michelmelo.pt) está fixo no pacote.
final push = PushService(
appId: 'uuid-publico-da-tua-app', // NÃO é um token — é público
);
2. Inicializa (faz tudo)
No main() (antes do runApp) ou no initState do widget raiz:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final push = PushService(appId: 'uuid-publico-da-tua-app');
final device = await push.initialize(
externalId: 'user-42', // opcional — id do user na tua app
tags: {'tier': 'vip'}, // opcional
onOpened: (notification) {
// utilizador tocou na notificação
final url = notification.data['url'];
},
onReceived: (notification) {
// notificação chegou com a app em primeiro plano
},
);
// ⬇ IMPORTANTE — guarda no teu backend:
// device.subscriptionId é o id estável deste device. É o que o teu servidor
// envia na REST API para lhe mandar pushes. Nunca uses o token FCM/APNs.
await meuBackend.guardarSubscriptionId(device.subscriptionId);
runApp(MyApp(push: push));
}
O initialize:
- Inicializa o Firebase.
- Pede permissão de notificações.
- Obtém o token FCM/APNs.
- Captura sozinho: modelo, versão do SO, versão da app, idioma e fuso horário.
- Regista o device no Push Service (endpoint público).
- Liga os listeners (foreground, background, aberta, token atualizado).
Quando o token FCM for atualizado, o pacote re-regista automaticamente o device
(o subscriptionId mantém-se o mesmo).
3. O ID do device — o que guardar
Quando o device se regista (initialize ou registerDevice), o servidor devolve
um subscriptionId (UUID público e estável). É este o ID que deves guardar
no teu backend — é ele que identifica o device para envios futuros.
Nunca uses o
playerId(token FCM/APNs) como referência: é apenas o token de envio e não serve como destino. E não uses oidnumérico interno da resposta.
O subscriptionId mantém-se estável mesmo que o token FCM seja atualizado,
ou se o device voltar a registar-se — o servidor reutiliza a linha existente
(faz restore do registo anterior), preservando o mesmo subscriptionId.
4. Liga o device ao utilizador (login)
Chama setExternalId depois do login/registo na tua app, com o ID do utilizador.
O servidor associa o device à conta.
await push.setExternalId('user-42'); // após o login
await push.clearExternalId(); // após o logout
O
externalIdé o conceito de "utilizador" do Push Service: associa o device a uma identidade da tua app. Não há um id separado de utilizador — o targeting por "um utilizador" passa sempre por obter o(s)subscriptionIddo device associado àqueleexternalId(ver secção 6).
5. Enviar pushes — do teu backend (REST API)
O envio é feito pelo teu servidor, com um token REST (secreto) criado no painel:
curl -X POST https://push.michelmelo.pt/api/v1/apps/{slug}/pushes \
-H "Authorization: Bearer {REST_KEY}" \
-H "Content-Type: application/json" \
-d '{
"title": "Olá!",
"body": "Mensagem",
"devices": ["subscription-id-guardado-no-passo-2"]
}'
O servidor resolve o token FCM/APNs real a partir do subscriptionId e entrega.
Também podes enviar por segment (plataforma, país, idioma, tags) — mas devices
e segment são mutuamente exclusivos.
6. Enviar um push de um utilizador para outro
Para enviar uma notificação a um utilizador específico, resolves o device desse
utilizador para o seu subscriptionId e passas esse valor em devices. O teu
backend tem de manter a associação externalId (teu id de user) → subscriptionId.
// A app regista-se e guarda a associação no teu backend:
final device = await push.initialize(externalId: 'user-42');
await meuBackend.guardarAssociacao('user-42', device.subscriptionId);
// Mais tarde, do teu servidor, para enviar do user A ao user B:
// 1. consulta o subscriptionId do teu user B por externalId
// 2. envia o push com devices: [subscriptionIdDoUserB]
curl -X POST https://push.michelmelo.pt/api/v1/apps/{slug}/pushes \
-H "Authorization: Bearer {REST_KEY}" \
-H "Content-Type: application/json" \
-d '{
"title": "Nova mensagem",
"body": "O utilizador A enviou-te algo",
"devices": ["subscription-id-do-user-B"]
}'
Resumindo o fluxo "user A → user B":
- Cada utilizador, ao entrar na app, regista-se (
initialize) e o teu backend guardaexternalId → subscriptionId. - Quando o user A quer enviar ao user B, o teu servidor consulta o
subscriptionIddo user B (pela associação guardada no passo 1). - Faz
POST /pushescomdevices: [<subscriptionId do user B>]. - O Push Service resolve o token real e entrega a notificação ao device do user B.
Não existe um endpoint "enviar por externalId" — o destino é sempre o
subscriptionId do device.
7. (Opcional) Enviar a partir da app
Se quiseres que a app consiga enviar pushes diretamente, passa também o token REST (atenção: token secreto numa app móvel é um risco — para testes apenas):
final push = PushService(
appId: 'uuid-publico-da-tua-app',
apiToken: 'REST_KEY_OPCIONAL', // só se a app for enviar pushes
);
await push.sendPush(
title: 'Olá!',
body: 'Mensagem',
devices: ['subscription-id-1', 'subscription-id-2'], // OU segment
);
8. Desativa notificações no device
await push.unsubscribe(); // deixa de receber pushes
await push.subscribe(); // volta a receber
9. Mensagens in-app
Além de pushes, o SDK exibe mensagens in-app: no arranque
de cada sessão busca as mensagens elegíveis para o device e mostra-as quando os
gatilhos forem cumpridos. Não precisas de fazer nada — o initialize já o faz.
Para o SDK mostrar a mensagem sozinho, passa um navigatorKey:
final navigatorKey = GlobalKey<NavigatorState>();
final push = PushService(appId: 'uuid-publico-da-tua-app');
await push.initialize(
navigatorKey: navigatorKey,
onInAppMessageAction: (message, button) {
// ações url / deep_link / push_permission (o "close" é tratado pelo SDK)
if (button.action == 'url') abrirUrl(button.value!);
},
);
// No teu MaterialApp:
MaterialApp(navigatorKey: navigatorKey, home: MyHome());
Sem navigatorKey, o SDK não desenha nada: chama onInAppMessage e é a tua app
que renderiza (podes usar o widget InAppMessageView já incluído no pacote).
Os gatilhos são configurados no painel (abrir a app, duração da sessão, desde a
última mensagem). Para gatilhos personalizados (custom), usa:
push.addTrigger('nivel', '3'); // cumpre o gatilho "nivel = 3"
push.removeTrigger('nivel');
push.clearTriggers();
push.checkInAppMessages(); // reavalia já os gatilhos
As impressões, cliques e dispensas são reportados automaticamente ao servidor (servem os relatórios e o controlo de frequência). Se renderizares a mensagem à tua maneira, reporta com:
push.reportInAppEvent(message.id, 'impression');
push.reportInAppEvent(message.id, 'click', buttonId: 'b1');
push.reportInAppEvent(message.id, 'dismiss');
Tratamento de erros
Todas as chamadas lançam PushServiceException em caso de erro:
try {
await push.initialize();
} on PushServiceException catch (e) {
print('${e.statusCode}: ${e.message}');
}
Exemplo completo
Vê a app de exemplo em example/.
Documentação da API
A API v1 do Push Service está documentada em https://push.michelmelo.pt/docs.
Libraries
- push_service_client
- Cliente Flutter para o Push Service.