Notifiche push in Flutter con Firebase: guida pratica
Come integrare le notifiche push in Flutter con Firebase Cloud Messaging: setup con FlutterFire, gestione di permessi e token e dei tre stati dell'app, con codice aggiornato.
Notifiche push in Flutter con Firebase: guida pratica
Le notifiche push sono uno degli strumenti più efficaci per riportare gli utenti nella tua app: un promemoria, un ordine spedito, un nuovo messaggio. In Flutter lo standard per gestirle è Firebase Cloud Messaging (FCM), che con una sola base di codice raggiunge sia Android sia iOS.
In questa guida vediamo passo passo come configurare le notifiche push in un'app Flutter e gestirle nei tre stati in cui può trovarsi l'app: aperta, in background e completamente chiusa.
Prerequisiti: un'app Flutter funzionante, un account Firebase (gratuito) e, per l'iOS, un account Apple Developer e un iPhone fisico — sul simulatore le notifiche push non arrivano, è una restrizione di Apple.
1. Collega l'app a Firebase con FlutterFire
Prima di iniziare ti servono un progetto già creato nella Firebase Console e la Firebase CLI installata, con login effettuato:
# Installa la Firebase CLI (richiede Node.js) e accedi
npm install -g firebase-tools
firebase loginFatto questo, installa la CLI di FlutterFire e lanciala: è lei che registra l'app su Firebase e genera automaticamente il file di configurazione:
dart pub global activate flutterfire_cli
flutterfire configure --project=il-tuo-project-idIl comando crea lib/firebase_options.dart, che contiene tutte le opzioni necessarie per l'inizializzazione, e configura le piattaforme selezionate (Android, iOS, web).
2. Aggiungi le dipendenze
Nel pubspec.yaml aggiungi i tre pacchetti che ti servono (usa le versioni più recenti da pub.dev):
dependencies:
firebase_core: ^latest
firebase_messaging: ^latest
flutter_local_notifications: ^latestPoi installa con flutter pub get. Il pacchetto flutter_local_notifications serve a mostrare le notifiche quando l'app è in primo piano, come vedremo tra poco.
3. Inizializza Firebase
L'inizializzazione va fatta prima di qualsiasi altra chiamata a Firebase, quindi nel main():
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'firebase_options.dart';
// Deve essere una funzione top-level (fuori da ogni classe)
@pragma('vm:entry-point')
Future<void> _firebaseBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
// Messaggio ricevuto con app in background o chiusa
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
FirebaseMessaging.onBackgroundMessage(_firebaseBackgroundHandler);
runApp(const MyApp());
}Attenzione a un dettaglio che fa perdere ore: il gestore dei messaggi in background deve essere una funzione top-level annotata con @pragma('vm:entry-point'), perché viene eseguito in un isolate separato. Se lo metti dentro una classe, non funzionerà.
4. Configura Android e iOS
Android. FCM richiede minSdkVersion 23. Da Android 13 in poi serve anche il permesso a runtime POST_NOTIFICATIONS: lo richiederai insieme agli altri nel passo successivo.
iOS. Apri il progetto in Xcode (ios/Runner.xcworkspace) e attiva le capability Push Notifications e Background Modes (spuntando Remote notifications). Poi genera una chiave di autenticazione APNs (file .p8) dall'Apple Developer Center e caricala su Firebase, in Impostazioni progetto → Cloud Messaging → configurazione app Apple. Senza questo passaggio, su iOS non arriverà nulla.
5. Chiedi il permesso e ottieni il token
Ora chiedi all'utente il permesso di ricevere notifiche e, se lo concede, recupera il token del dispositivo: è l'identificativo a cui invierai i messaggi.
final messaging = FirebaseMessaging.instance;
final settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
final token = await messaging.getToken();
// Invia il token al tuo backend
}
// Il token può cambiare: intercetta l'aggiornamento e risincronizza
messaging.onTokenRefresh.listen((newToken) {
// Aggiorna il token sul backend
});
6. Inizializza il plugin e crea il canale
Per far comparire le notifiche mentre l'app è aperta, il plugin flutter_local_notifications ha bisogno di due cose: essere inizializzato e avere un canale su cui appoggiarsi. Su Android il canale è obbligatorio dalla versione 8 in poi — ogni notifica deve appartenere a un canale già registrato nel sistema, altrimenti viene semplicemente ignorata.
Conviene raccogliere tutto in un'unica funzione, così la lanci una volta sola all'avvio dell'app:
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
// Il canale Android va dichiarato una volta sola (obbligatorio da Android 8)
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel',
'Notifiche importanti',
description: 'Canale per le notifiche più importanti',
importance: Importance.max,
);
Future<void> setupNotifiche() async {
// 1. Inizializza il plugin: icona Android + impostazioni iOS
const initSettings = InitializationSettings(
android: AndroidInitializationSettings('@mipmap/ic_launcher'),
iOS: DarwinInitializationSettings(),
);
await flutterLocalNotificationsPlugin.initialize(initSettings);
// 2. Crea il canale su Android (senza, la notifica in primo piano non appare)
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
// 3. Su iOS: mostra la notifica anche con app aperta
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
}Fatto questo, richiama setupNotifiche() dentro il main():
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await setupNotifiche();
FirebaseMessaging.onBackgroundMessage(_firebaseBackgroundHandler);Un'accortezza sul canale: l'id che gli assegni (high_importance_channel) va riutilizzato identico ogni volta che mostri una notifica su quel canale. Se lo cambi anche di poco, Android lo considera un canale nuovo e perde la priorità e il suono che gli hai impostato qui.
7. Gestisci i messaggi nei tre stati
Qui sta il vero lavoro. Flutter gestisce le notifiche in modo diverso a seconda di dove si trova l'app.
App in primo piano. Su Android, quando l'app è aperta, FCM non mostra la notifica automaticamente: la ricevi tramite uno stream e la visualizzi tu con flutter_local_notifications. Su iOS non devi fare nulla di tutto questo, perché la comparsa in primo piano è già gestita da setForegroundNotificationPresentationOptions.
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
// Serve soprattutto su Android: su iOS la notifica in primo piano è già gestita
final notification = message.notification;
if (notification != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
const NotificationDetails(
android: AndroidNotificationDetails(
'high_importance_channel',
'Notifiche importanti',
importance: Importance.max,
priority: Priority.high,
),
),
);
}
});Tap sulla notifica. Quando l'utente tocca una notifica, di solito vuoi portarlo su una schermata specifica (ad esempio la chat giusta). Ci sono due casi da coprire:
// App aperta da stato completamente chiuso
final initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
_gestisciTap(initialMessage);
}
// App in background e riportata in primo piano dal tap
FirebaseMessaging.onMessageOpenedApp.listen(_gestisciTap);App in background o chiusa. Se ne occupa il _firebaseBackgroundHandler che hai registrato nel main(): quando il messaggio contiene una parte notification, il sistema mostra da solo la notifica nella barra di stato.
8. Invia una notifica di prova
Il modo più veloce per testare: vai nella Firebase Console → Cloud Messaging → Invia messaggio di prova e incolla il token del dispositivo.
Dal tuo server, invece, usa l'API HTTP v1 di FCM. La vecchia API con la "server key" è stata dismessa: oggi serve un access token OAuth2 generato da un service account. La richiesta ha questa forma:
POST https://fcm.googleapis.com/v1/projects/IL_TUO_PROJECT_ID/messages:send
Authorization: Bearer <ACCESS_TOKEN>
Content-Type: application/json
{
"message": {
"token": "<TOKEN_DEL_DISPOSITIVO>",
"notification": {
"title": "Ciao!",
"body": "Hai una nuova notifica"
}
}
}L'access token si ottiene dalla chiave del service account (il file JSON scaricabile da Impostazioni progetto → Account di servizio), tipicamente con l'aiuto di una libreria come google-auth sul backend.
Errori comuni (e come evitarli)
Tre problemi che capitano quasi a tutti. La notifica doppia in primo piano: succede quando mostri la notifica locale anche per messaggi che contengono già una parte notification gestita dal sistema — mostra la locale solo per i messaggi di soli dati (data-only). Niente notifiche su iOS: quasi sempre è la chiave APNs mancante, oppure stai testando sul simulatore. Su Android il messaggio data-only non compare: i messaggi senza parte notification non vengono mostrati in automatico, devi visualizzarli tu nel background handler.
Domande frequenti
Le notifiche push funzionano sul simulatore iOS?
No. Sul simulatore iOS le push non arrivano: è una restrizione di Apple. Per testarle ti serve un iPhone fisico con una configurazione APNs valida. Su Android, invece, l'emulatore con i servizi Google Play va benissimo.
Perché su iOS non arriva nessuna notifica?
Nella quasi totalità dei casi è la chiave APNs mancante o non caricata su Firebase, oppure stai testando sul simulatore. Controlla di aver generato il file .p8, di averlo caricato in Cloud Messaging e di aver attivato le capability Push Notifications e Background Modes in Xcode.
Come gestisco i messaggi data-only (senza parte notification)?
I messaggi che contengono solo il campo data non vengono mostrati in automatico da nessuna delle due piattaforme: li ricevi nel tuo handler (onMessage in primo piano, il background handler ad app chiusa) e sei tu a costruire la notifica con flutter_local_notifications. Sono utili quando vuoi decidere lato codice se, quando e come mostrare la notifica.
Le notifiche arrivano anche ad app completamente chiusa?
Sì, se il messaggio contiene una parte notification: in quel caso è il sistema operativo a mostrarla nella barra di stato, senza che la tua app sia in esecuzione. Quando l'utente tocca la notifica, l'app si riapre e recuperi il messaggio con getInitialMessage.
In sintesi
Configurare le notifiche push in Flutter con FCM richiede pochi passaggi ben precisi: collegare l'app con FlutterFire, gestire permessi e token, e — la parte che fa davvero la differenza — trattare correttamente i tre stati dell'app. Una volta impostato bene, funziona in modo affidabile su iOS e Android con un'unica base di codice.
Vuoi integrare notifiche push affidabili nella tua app, magari collegate al tuo gestionale o backend? Raccontaci il tuo progetto: ti aiutiamo a farlo nel modo giusto.