Notifiche push in Flutter con Firebase Cloud Messaging
GuideIntermedio40 min Flutter 3.x

Notifiche push in Flutter con Firebase Cloud Messaging

Le notifiche push sono uno strumento fondamentale per coinvolgere gli utenti anche quando l'app non è in uso. In questo tutorial integreremo Firebase Cloud Messaging (FCM) in un'app Flutter, imparando a configurare il progetto, ottenere il token del dispositivo e gestire i messaggi nei tre stati principali dell'app: in primo piano, in background e completamente chiusa.

Partiamo dal presupposto di avere già un progetto Firebase collegato tramite FlutterFire. Vedremo anche come mostrare una notifica visibile quando l'app è in foreground, dato che FCM di default non lo fa automaticamente su Android.

  1. 1

    Aggiungere le dipendenze

    Assicurati di avere già configurato Firebase nel progetto tramite flutterfire configure. Poi aggiungi i pacchetti necessari: firebase_core per l'inizializzazione, firebase_messaging per FCM e flutter_local_notifications per mostrare le notifiche in foreground su Android.

    Esegui il comando da terminale o aggiorna manualmente il pubspec.yaml.

    flutter pub add firebase_core firebase_messaging flutter_local_notifications

    Risultato atteso

    I pacchetti vengono scaricati e appaiono tra le dipendenze del pubspec.yaml.

  2. 2

    Configurare la piattaforma Android

    Per Android, verifica che nel file android/app/build.gradle il minSdkVersion sia almeno 21. Non è richiesta alcuna chiave particolare perché il file google-services.json generato da FlutterFire contiene già la configurazione FCM.

    Su iOS occorre abilitare le capability Push Notifications e Background Modes > Remote notifications in Xcode, oltre a caricare la chiave APNs nella console Firebase. In questo tutorial ci concentreremo sul flusso Dart comune alle due piattaforme.

    // android/app/build.gradle
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 34
    }

    Risultato atteso

    Il progetto compila correttamente su Android con le impostazioni FCM pronte.

  3. 3

    Inizializzare Firebase e richiedere i permessi

    Nel main() inizializziamo Firebase prima di avviare l'app. Su iOS (e Android 13+) è necessario richiedere esplicitamente il permesso di notifica con requestPermission().

    Stampiamo lo stato di autorizzazione per verificare che l'utente abbia accettato.

    import 'package:flutter/material.dart';
    import 'package:firebase_core/firebase_core.dart';
    import 'package:firebase_messaging/firebase_messaging.dart';
    import 'firebase_options.dart';
    
    Future<void> main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await Firebase.initializeApp(
        options: DefaultFirebaseOptions.currentPlatform,
      );
    
      final messaging = FirebaseMessaging.instance;
      final settings = await messaging.requestPermission(
        alert: true,
        badge: true,
        sound: true,
      );
      debugPrint('Permesso notifiche: ${settings.authorizationStatus}');
    
      runApp(const MyApp());
    }

    Risultato atteso

    All'avvio l'app mostra la richiesta di permesso e nei log compare 'authorized' se l'utente accetta.

  4. 4

    Ottenere il token FCM del dispositivo

    Ogni dispositivo ha un token univoco che identifica l'installazione dell'app. Questo token va inviato al tuo backend per poter indirizzare notifiche a utenti specifici.

    Recuperiamo il token e ascoltiamo l'evento onTokenRefresh, che si attiva quando il token viene rigenerato (ad esempio dopo la reinstallazione).

    Future<void> _setupToken() async {
      final messaging = FirebaseMessaging.instance;
    
      final token = await messaging.getToken();
      debugPrint('Token FCM: $token');
      // TODO: invia il token al tuo backend
    
      messaging.onTokenRefresh.listen((newToken) {
        debugPrint('Token aggiornato: $newToken');
        // TODO: aggiorna il token sul backend
      });
    }

    Risultato atteso

    Nei log compare un token alfanumerico lungo che puoi copiare per i test dalla console Firebase.

  5. 5

    Gestire i messaggi in background e ad app chiusa

    Quando l'app è in background o chiusa, i messaggi con solo payload dati vengono gestiti da un handler top-level annotato con @pragma('vm:entry-point'). Questa funzione deve stare fuori da qualsiasi classe.

    Registriamo l'handler nel main() con onBackgroundMessage. Nota: le notifiche con payload notification vengono mostrate automaticamente dal sistema quando l'app non è in foreground.

    @pragma('vm:entry-point')
    Future<void> _firebaseBackgroundHandler(RemoteMessage message) async {
      await Firebase.initializeApp(
        options: DefaultFirebaseOptions.currentPlatform,
      );
      debugPrint('Messaggio in background: ${message.messageId}');
      debugPrint('Dati: ${message.data}');
    }
    
    // Nel main(), dopo Firebase.initializeApp:
    FirebaseMessaging.onBackgroundMessage(_firebaseBackgroundHandler);

    Risultato atteso

    Inviando una notifica ad app chiusa, il sistema mostra la notifica; toccandola l'app si apre e i dati sono disponibili.

  6. 6

    Mostrare le notifiche in foreground

    In foreground FCM non mostra automaticamente la notifica. Usiamo flutter_local_notifications per visualizzarla manualmente ascoltando lo stream onMessage.

    Creiamo un canale di notifica su Android e mostriamo la notifica quando arriva un messaggio mentre l'app è aperta.

    final _localNotifications = FlutterLocalNotificationsPlugin();
    
    const _channel = AndroidNotificationChannel(
      'high_importance_channel',
      'Notifiche importanti',
      importance: Importance.high,
    );
    
    Future<void> _initLocalNotifications() async {
      const initSettings = InitializationSettings(
        android: AndroidInitializationSettings('@mipmap/ic_launcher'),
        iOS: DarwinInitializationSettings(),
      );
      await _localNotifications.initialize(initSettings);
    
      await _localNotifications
          .resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin>()
          ?.createNotificationChannel(_channel);
    
      FirebaseMessaging.onMessage.listen((message) {
        final notification = message.notification;
        if (notification != null) {
          _localNotifications.show(
            notification.hashCode,
            notification.title,
            notification.body,
            NotificationDetails(
              android: AndroidNotificationDetails(
                _channel.id,
                _channel.name,
                importance: Importance.high,
                icon: '@mipmap/ic_launcher',
              ),
            ),
          );
        }
      });
    }

    Risultato atteso

    Con l'app aperta, all'arrivo di una notifica compare un banner locale con titolo e corpo del messaggio.

  7. 7

    Gestire il tap sulla notifica

    Infine gestiamo l'apertura dell'app tramite tocco sulla notifica. Usiamo getInitialMessage() per il caso in cui l'app era chiusa e onMessageOpenedApp per quando era in background.

    Qui puoi navigare verso una schermata specifica in base al payload ricevuto.

    Future<void> _setupInteraction() async {
      // App aperta da stato terminato
      final initialMessage =
          await FirebaseMessaging.instance.getInitialMessage();
      if (initialMessage != null) {
        _handleMessage(initialMessage);
      }
    
      // App aperta da background
      FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage);
    }
    
    void _handleMessage(RemoteMessage message) {
      final route = message.data['route'];
      debugPrint('Apertura tramite notifica, route: $route');
      // TODO: naviga verso la schermata indicata
    }

    Risultato atteso

    Toccando una notifica con un campo dati 'route', l'app registra il valore e può reindirizzare l'utente alla schermata corretta.

CondividiXLinkedInFacebookWhatsApp

Commenti (0)

Ancora nessun commento. Inizia tu!