Introduzione
Le notifiche push sono uno strumento fondamentale per coinvolgere gli utenti di un'app mobile: avvisano di nuovi messaggi, promozioni o aggiornamenti anche quando l'app è chiusa. In Flutter, la soluzione più diffusa e gratuita è Firebase Cloud Messaging (FCM), integrabile tramite i pacchetti firebase_core e firebase_messaging.
In questa guida vedremo come configurare FCM, ricevere notifiche nei diversi stati dell'app (foreground, background, terminata) e mostrare notifiche locali con flutter_local_notifications.
Configurazione iniziale
Aggiungi le dipendenze al tuo pubspec.yaml:
dependencies:
firebase_core: ^3.6.0
firebase_messaging: ^15.1.3
flutter_local_notifications: ^18.0.1
Dopo aver creato un progetto su Firebase Console, usa la FlutterFire CLI per generare la configurazione:
dart pub global activate flutterfire_cli
flutterfire configure
Questo comando crea il file firebase_options.dart e configura automaticamente Android e iOS.
Inizializzazione di Firebase
Inizializza Firebase nel main() prima di avviare l'app:
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
Richiedere i permessi
Su iOS (e su Android 13+) è obbligatorio richiedere il permesso all'utente per ricevere notifiche:
import 'package:firebase_messaging/firebase_messaging.dart';
Future<void> requestPermission() async {
final messaging = FirebaseMessaging.instance;
final settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
print('Permesso notifiche concesso');
}
}
Ottenere il token FCM
Ogni dispositivo possiede un token univoco che identifica la destinazione delle notifiche. Va inviato al tuo backend per indirizzare i messaggi:
final token = await FirebaseMessaging.instance.getToken();
print('Token FCM: $token');
// Ascolta i rinnovi del token
FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {
// Invia il nuovo token al backend
});
Gestire i messaggi nei tre stati
FCM si comporta diversamente a seconda dello stato dell'app.
App in foreground
Quando l'app è aperta, le notifiche non vengono mostrate automaticamente: dobbiamo gestirle noi, tipicamente mostrando una notifica locale.
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
final notification = message.notification;
if (notification != null) {
showLocalNotification(
notification.title ?? '',
notification.body ?? '',
);
}
});
App in background o terminata (tap sulla notifica)
Quando l'utente tocca una notifica con l'app in background:
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
// Naviga verso una schermata specifica
final route = message.data['route'];
if (route != null) {
navigatorKey.currentState?.pushNamed(route);
}
});
Se l'app era completamente chiusa, recupera il messaggio iniziale:
final initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
// Gestisci la navigazione di avvio
}
Handler in background
Per elaborare messaggi anche quando l'app non è in foreground, registra un handler top-level (fuori da qualsiasi classe):
@pragma('vm:entry-point')
Future<void> firebaseBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print('Messaggio in background: ${message.messageId}');
}
void main() {
FirebaseMessaging.onBackgroundMessage(firebaseBackgroundHandler);
// ...
}
L'annotazione
@pragma('vm:entry-point')è essenziale: impedisce al tree-shaking di rimuovere la funzione nelle build release.
Notifiche locali in foreground
Configuriamo flutter_local_notifications per mostrare le notifiche quando l'app è aperta:
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
final localNotifications = FlutterLocalNotificationsPlugin();
Future<void> initLocalNotifications() async {
const androidSettings =
AndroidInitializationSettings('@mipmap/ic_launcher');
const iosSettings = DarwinInitializationSettings();
const settings = InitializationSettings(
android: androidSettings,
iOS: iosSettings,
);
await localNotifications.initialize(settings);
}
Future<void> showLocalNotification(String title, String body) async {
const androidDetails = AndroidNotificationDetails(
'high_importance_channel',
'Notifiche importanti',
importance: Importance.max,
priority: Priority.high,
);
const details = NotificationDetails(android: androidDetails);
await localNotifications.show(0, title, body, details);
}
Su Android è inoltre consigliato creare il canale di notifica con la stessa id usata nei dettagli, altrimenti su Android 8+ le notifiche potrebbero non apparire.
Best practice
- Richiedi i permessi al momento giusto: non al primo avvio, ma quando l'utente comprende il valore delle notifiche.
- Usa i
data messageoltre ainotification messageper veicolare informazioni di routing. - Salva e aggiorna il token sul backend, gestendo anche
onTokenRefresh. - Testa tutti gli stati: foreground, background e app terminata si comportano diversamente.
- Configura i canali su Android per dare all'utente controllo granulare.
Conclusioni
Integrare le notifiche push con Firebase Cloud Messaging in Flutter richiede pochi passaggi, ma è fondamentale gestire correttamente i diversi stati dell'app e combinare FCM con le notifiche locali per un'esperienza coerente. Con i frammenti visti in questa guida hai una base solida e pronta per la produzione.
