push_service_client 1.0.0
push_service_client: ^1.0.0 copied to clipboard
Cliente Flutter para o Push Service — regista devices sozinho (estilo OneSignal) e recebe push notifications (FCM/APNs).
example/lib/main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:push_service_client/push_service_client.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// TODO: substituir pelo App ID público da tua app no Push Service
// (painel → Aplicações → API → App ID). Não é secreto.
final push = PushService(
appId: 'UUID-PUBLICO-DA-TUA-APP',
);
final device = await push.initialize(
tags: {'app': 'exemplo'},
onOpened: (notification) {
debugPrint('Push aberto: ${notification.title} — ${notification.data}');
},
onReceived: (notification) {
debugPrint('Push recebido: ${notification.title}');
},
);
// Guarda device.subscriptionId no teu backend — é o id para enviar pushes.
debugPrint('Subscription ID: ${device.subscriptionId}');
runApp(PushServiceExampleApp(push: push));
}
class PushServiceExampleApp extends StatefulWidget {
const PushServiceExampleApp({super.key, required this.push});
final PushService push;
@override
State<PushServiceExampleApp> createState() => _PushServiceExampleAppState();
}
/// Envia atividade (sessões e tempo de uso) para o Push Service quando a app
/// vai para segundo plano, para o painel mostrar dados reais.
class _ActivityTracker extends WidgetsBindingObserver {
_ActivityTracker(this.push);
final PushService push;
DateTime? _foregroundSince;
bool _sessionSent = false;
/// Marca o início da sessão no servidor (primeira sessão incluída).
Future<void> start() async {
final device = push.device;
if (device == null || _sessionSent) return;
_sessionSent = true;
_foregroundSince = DateTime.now();
try {
await push.trackActivity(
sessions: 1,
firstSessionAt: device.firstSessionAt ?? DateTime.now(),
);
} catch (_) {
// Sem rede — tenta na próxima mudança de estado.
}
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
final device = push.device;
if (device == null) return;
if (state == AppLifecycleState.resumed) {
_foregroundSince = DateTime.now();
return;
}
if (state == AppLifecycleState.inactive ||
state == AppLifecycleState.paused ||
state == AppLifecycleState.hidden ||
state == AppLifecycleState.detached) {
final since = _foregroundSince;
final duration = since != null
? DateTime.now().difference(since).inSeconds
: 0;
if (duration > 0) {
unawaited(() async {
try {
await push.trackActivity(
usageDuration: duration,
sessionDuration: duration,
sessionStartedAt: since,
sessionEndedAt: DateTime.now(),
);
} catch (_) {
// Sem rede — tenta na próxima mudança de estado.
}
}());
}
}
}
}
class _PushServiceExampleAppState extends State<PushServiceExampleApp> {
late final _ActivityTracker _tracker;
@override
void initState() {
super.initState();
_tracker = _ActivityTracker(widget.push);
WidgetsBinding.instance.addObserver(_tracker);
_tracker.start();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(_tracker);
super.dispose();
}
String get _status => widget.push.device != null
? 'Device registado!'
: 'A registar...';
Future<void> _unsubscribe() async {
if (widget.push.device == null) return;
try {
await widget.push.unsubscribe();
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Device desativado (não recebe pushes).')),
);
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Erro: $e')),
);
}
}
@override
Widget build(BuildContext context) {
final device = widget.push.device;
return MaterialApp(
title: 'Push Service Exemplo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF576CDB),
brightness: Brightness.light,
),
useMaterial3: true,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Push Service — Exemplo'),
backgroundColor: const Color(0xFF576CDB),
foregroundColor: Colors.white,
),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Estado',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Text(_status),
if (device != null) ...[
const SizedBox(height: 8),
Text(
'Subscription ID: ${device.subscriptionId}'),
Text('Plataforma: ${device.platform}'),
Text('Subscrito: ${device.subscribed}'),
],
],
),
),
),
const SizedBox(height: 16),
OutlinedButton.icon(
onPressed: device == null ? null : _unsubscribe,
icon: const Icon(Icons.notifications_off),
label: const Text('Desativar notificações'),
),
const Spacer(),
const Text(
'Configura o App ID público da tua app no ficheiro main.dart '
'para ligar ao teu Push Service. Não te esqueças de adicionar '
'o google-services.json (Android) ou GoogleService-Info.plist (iOS).',
style: TextStyle(fontSize: 12, color: Colors.grey),
),
],
),
),
),
);
}
}