|

Flutter Firebase Push Notifications Complete Guide

Flutter Firebase Push Notifications
Flutter Firebase Push Notifications

Prerequisites

  • Flutter SDK installed
  • Firebase CLI installed (npm install -g firebase-tools)
  • A Firebase project created in the Firebase Console

Step 1: Firebase Project Setup

  1. Go to Firebase Console
  2. Create a new project or select an existing one
  3. Enable Cloud Messaging in the Firebase Console:
    • Go to Project Settings > Cloud Messaging
    • Note down your Server Key and Sender ID

Step 2: Flutter Project Configuration

Add Dependencies

Add these packages to your pubspec.yaml:

dependencies:
  firebase_core: ^2.24.2
  firebase_messaging: ^14.7.10
  flutter_local_notifications: ^16.3.2

Run flutter pub get after adding dependencies.

Initialize Firebase

Install FlutterFire CLI:

dart pub global activate flutterfire_cli

Configure Firebase for your Flutter project:

flutterfire configure

This will create firebase_options.dart and configure both Android and iOS.

Step 3: Android Configuration

Update android/app/build.gradle

android {
    compileSdkVersion 33 // or higher
    
    defaultConfig {
        minSdkVersion 19
        targetSdkVersion 33
    }
}

Add Permissions in android/app/src/main/AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Add these permissions -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    
    <application>
        <!-- Add this service for background messages -->
        <service
            android:name="io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingBackgroundService"
            android:exported="false" />
            
        <!-- Add this for notification icon -->
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_icon"
            android:resource="@drawable/ic_launcher" />
            
        <!-- Add this for notification color -->
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_color"
            android:resource="@color/colorAccent" />
    </application>
</manifest>

Step 4: iOS Configuration

Update ios/Runner/Info.plist

No additional configuration needed for basic setup.

See also  State Management Techniques in Flutter 3.35

Enable Push Notifications Capability

  1. Open ios/Runner.xcworkspace in Xcode
  2. Select your project target
  3. Go to “Signing & Capabilities”
  4. Click “+” and add “Push Notifications”
  5. Add “Background Modes” and check “Background fetch” and “Remote notifications”

Step 5: Flutter Implementation

Main Application Setup

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'firebase_options.dart';

// Background message handler
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  print('Handling a background message: ${message.messageId}');
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Initialize Firebase
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  
  // Set background message handler
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Firebase Push Notifications',
      home: NotificationScreen(),
    );
  }
}

Notification Service Implementation

class NotificationService {
  static final FlutterLocalNotificationsPlugin _localNotifications =
      FlutterLocalNotificationsPlugin();
  static final FirebaseMessaging _messaging = FirebaseMessaging.instance;

  // Initialize notifications
  static Future<void> initialize() async {
    // Request permissions
    NotificationSettings settings = await _messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
      carPlay: false,
      criticalAlert: false,
      provisional: false,
      announcement: false,
    );

    print('User granted permission: ${settings.authorizationStatus}');

    // Initialize local notifications
    const AndroidInitializationSettings initializationSettingsAndroid =
        AndroidInitializationSettings('@mipmap/ic_launcher');

    const DarwinInitializationSettings initializationSettingsIOS =
        DarwinInitializationSettings(
      requestSoundPermission: true,
      requestBadgePermission: true,
      requestAlertPermission: true,
    );

    const InitializationSettings initializationSettings =
        InitializationSettings(
      android: initializationSettingsAndroid,
      iOS: initializationSettingsIOS,
    );

    await _localNotifications.initialize(
      initializationSettings,
      onDidReceiveNotificationResponse: onNotificationTapped,
    );

    // Get FCM token
    String? token = await _messaging.getToken();
    print('FCM Token: $token');

    // Handle foreground messages
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      print('Received a message while in foreground!');
      print('Message data: ${message.data}');

      if (message.notification != null) {
        print('Message also contained a notification: ${message.notification}');
        _showLocalNotification(message);
      }
    });

    // Handle notification taps when app is terminated
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      print('A new onMessageOpenedApp event was published!');
      _handleNotificationTap(message);
    });

    // Check for initial message if app was opened from terminated state
    RemoteMessage? initialMessage = await _messaging.getInitialMessage();
    if (initialMessage != null) {
      _handleNotificationTap(initialMessage);
    }
  }

  // Show local notification
  static Future<void> _showLocalNotification(RemoteMessage message) async {
    const AndroidNotificationDetails androidPlatformChannelSpecifics =
        AndroidNotificationDetails(
      'high_importance_channel',
      'High Importance Notifications',
      channelDescription: 'This channel is used for important notifications.',
      importance: Importance.max,
      priority: Priority.high,
      showWhen: false,
    );

    const DarwinNotificationDetails iOSPlatformChannelSpecifics =
        DarwinNotificationDetails();

    const NotificationDetails platformChannelSpecifics = NotificationDetails(
      android: androidPlatformChannelSpecifics,
      iOS: iOSPlatformChannelSpecifics,
    );

    await _localNotifications.show(
      0,
      message.notification?.title,
      message.notification?.body,
      platformChannelSpecifics,
      payload: message.data.toString(),
    );
  }

  // Handle notification tap
  static void onNotificationTapped(NotificationResponse notificationResponse) {
    print('Notification tapped: ${notificationResponse.payload}');
    // Handle navigation or actions here
  }

  static void _handleNotificationTap(RemoteMessage message) {
    print('Notification tapped with data: ${message.data}');
    // Handle navigation based on message data
  }

  // Subscribe to topic
  static Future<void> subscribeToTopic(String topic) async {
    await _messaging.subscribeToTopic(topic);
    print('Subscribed to topic: $topic');
  }

  // Unsubscribe from topic
  static Future<void> unsubscribeFromTopic(String topic) async {
    await _messaging.unsubscribeFromTopic(topic);
    print('Unsubscribed from topic: $topic');
  }
}

Sample Screen Implementation

class NotificationScreen extends StatefulWidget {
  @override
  _NotificationScreenState createState() => _NotificationScreenState();
}

class _NotificationScreenState extends State<NotificationScreen> {
  String? _fcmToken;

  @override
  void initState() {
    super.initState();
    _initializeNotifications();
  }

  Future<void> _initializeNotifications() async {
    await NotificationService.initialize();
    
    // Get and display FCM token
    _fcmToken = await FirebaseMessaging.instance.getToken();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Firebase Push Notifications'),
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'FCM Token:',
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
            ),
            SizedBox(height: 8),
            Container(
              padding: EdgeInsets.all(8),
              decoration: BoxDecoration(
                border: Border.all(color: Colors.grey),
                borderRadius: BorderRadius.circular(4),
              ),
              child: SelectableText(
                _fcmToken ?? 'Loading...',
                style: TextStyle(fontSize: 12),
              ),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => NotificationService.subscribeToTopic('general'),
              child: Text('Subscribe to General Topic'),
            ),
            ElevatedButton(
              onPressed: () => NotificationService.unsubscribeFromTopic('general'),
              child: Text('Unsubscribe from General Topic'),
            ),
          ],
        ),
      ),
    );
  }
}

Step 6: Testing Push Notifications

Method 1: Firebase Console

  1. Go to Firebase Console > Cloud Messaging
  2. Click “Send your first message”
  3. Enter title and message
  4. Select your app
  5. Send test message
See also  Why Good Programmers Hate GetX for Flutter Projects?

Method 2: Using FCM HTTP API

curl -X POST https://fcm.googleapis.com/fcm/send \
  -H "Authorization: key=YOUR_SERVER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "FCM_TOKEN_HERE",
    "notification": {
      "title": "Test Notification",
      "body": "This is a test message"
    },
    "data": {
      "custom_key": "custom_value"
    }
  }'

Method 3: Topic-based messaging

curl -X POST https://fcm.googleapis.com/fcm/send \
  -H "Authorization: key=YOUR_SERVER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "/topics/general",
    "notification": {
      "title": "Topic Notification",
      "body": "This message was sent to topic subscribers"
    }
  }'

Troubleshooting

Common Issues

  1. Notifications not received on iOS
    • Ensure you have added Push Notifications capability
    • Check that your APNs certificate is properly configured in Firebase Console
  2. Notifications not working in release mode
    • Make sure you’ve configured the background message handler
    • Check that the app has notification permissions
  3. Token is null
    • Ensure Firebase is properly initialized
    • Check internet connection
    • Verify Firebase configuration files are correct
  4. Background notifications not working
    • Ensure background message handler is set up correctly
    • Check that the app has background refresh permissions

Testing Checklist

  • [ ] App receives notifications when in foreground
  • [ ] App receives notifications when in background
  • [ ] App receives notifications when terminated
  • [ ] Notification taps open the app correctly
  • [ ] FCM token is generated successfully
  • [ ] Topic subscription/unsubscription works
  • [ ] Custom data is passed correctly

Additional Features

Custom Notification Channels (Android)

// Create custom notification channel
await _localNotifications
    .resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
    ?.createNotificationChannel(
      AndroidNotificationChannel(
        'custom_channel',
        'Custom Notifications',
        description: 'Custom notification channel',
        importance: Importance.high,
      ),
    );

Scheduled Local Notifications

// Schedule a notification
await _localNotifications.zonedSchedule(
  0,
  'Scheduled Notification',
  'This notification was scheduled',
  tz.TZDateTime.now(tz.local).add(Duration(seconds: 10)),
  platformChannelSpecifics,
  uiLocalNotificationDateInterpretation:
      UILocalNotificationDateInterpretation.absoluteTime,
);

This setup provides a complete foundation for Firebase push notifications in Flutter, including foreground, background, and terminated state handling.

See also  Firebase Integration: Auth, Firestore, and Realtime Database

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *