iOS Development

flutter – FirebaseMessaging notification duplicated on iOS when together with an imageUrl

Spread the love

I am doing a Flutter instance app for FirebaseMessaging to check out how server notification would work. So I’ve adopted the information on FlutterFire and setup every little thing and it really works nice on Android.

So far as iOS goes, it appears to work nice so long as I do not embrace photos within the notification. Once I do embrace an imageUrl, the app exhibits a notification with all the content material aside from the Picture, and instantly after, one other with the identical content material and the picture.

I am fairly misplaced, I do not perceive why it could try this. Whether or not I exploit the take a look at notification from the firebase console or my very own backend utilizing FirebaseMessaging.ship, the conduct is identical.

Listed here are snippets of the code:


import 'dart:developer';
import 'dart:io';

import 'package deal:firebase_messaging/firebase_messaging.dart';
import 'package deal:flutter/materials.dart';
import 'package deal:firebase_core/firebase_core.dart';
import 'package deal:flutter_local_notifications/flutter_local_notifications.dart';
import 'package deal:googlenotifpoc/notification_manager.dart';
import 'firebase_options.dart';

void primary() async {
  await Firebase.initializeApp(
    title: "...",
    choices: DefaultFirebaseOptions.currentPlatform,
  await FirebaseMessaging.occasion.setAutoInitEnabled(true);

  const AndroidNotificationChannel channel = AndroidNotificationChannel(
    'high_importance_channel', // id
    'Excessive Significance Notifications', // title
        'This channel is used for necessary notifications.', // description
    significance: Significance.max,

  const AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
        'high_importance_channel', // id
        'Excessive Significance Notifications',
        channelDescription: 'This channel is used for necessary notifications.',
        significance: Significance.max,
        precedence: Precedence.excessive,
        ticker: 'ticker',
        icon: "ic_white",
        playSound: true,

  await FirebaseMessaging.occasion.setForegroundNotificationPresentationOptions(
    alert: true, // Required to show a heads up notification
    badge: true,
    sound: true,

  ultimate FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =

  await flutterLocalNotificationsPlugin

  FirebaseMessaging.onMessage.hear((occasion) => NotificationManager.showNotification(occasion, androidDetails),);

  runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({tremendous.key});

  Widget construct(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
      dwelling: const MyHomePage(title: 'Flutter Demo Residence Web page'),

class MyHomePage extends StatefulWidget {
  const MyHomePage({tremendous.key, required this.title});

  ultimate String title;

  State<MyHomePage> createState() => _MyHomePageState();

class _MyHomePageState extends State<MyHomePage> {
  Widget construct(BuildContext context) {
    FirebaseMessaging messaging = FirebaseMessaging.occasion;

      alert: true,
      announcement: false,
      badge: true,
      carPlay: true,
      criticalAlert: false,
      provisional: false,
      sound: true,
    return Scaffold(
      appBar: AppBar(
        title: Textual content(widget.title),
      physique: Middle(
        little one: Column(
          mainAxisAlignment: MainAxisAlignment.middle,
          kids: <Widget>[
              future: getFMCToken(),
              builder: (BuildContext context, AsyncSnapshot<String?> snapshot) {
                if (snapshot.hasData) {
                  log( ?? "-");
                  print( ?? "-");
                  return Text(
                    "FMC TOKEN: n ${}",
                    style: const TextStyle(
                        fontSize: 16, fontWeight: FontWeight.bold),
                } else {
                  return const Text("Loading your FCM token...");

  Future<String?> getFMCToken() async {
    String? apnsToken;
    if(Platform.isIOS) {
      apnsToken = await FirebaseMessaging.occasion.getAPNSToken();
      log("Acquired APNS token: $apnsToken");
      print("Acquired APNS token: $apnsToken");

    if(Platform.isAndroid || apnsToken != null) {
      return FirebaseMessaging.occasion.getToken();

    return Future.worth(null);

NotificationManaging class

import 'package deal:firebase_messaging/firebase_messaging.dart';
import 'package deal:flutter_local_notifications/flutter_local_notifications.dart';

class NotificationManager {

  static Future<void> showNotification(RemoteMessage payload, AndroidNotificationDetails androidDetails) async {
    const android = AndroidInitializationSettings("ic_white");
    const initializationSettingsIOS = DarwinInitializationSettings();
    const initialSetting = InitializationSettings(android: android, iOS: initializationSettingsIOS);
    ultimate FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

    const iOSDetails = DarwinNotificationDetails();
    NotificationDetails platformChannelSpecifics = NotificationDetails(android: androidDetails, iOS: iOSDetails);

    await flutterLocalNotificationsPlugin.present(0, payload.notification!.title, payload.notification!.physique, platformChannelSpecifics);

ios notificationService.m

//  NotificationService.m
//  ImageNotification

#import "NotificationService.h"
#import "FirebaseMessaging.h"

@interface NotificationService ()

@property (nonatomic, sturdy) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, sturdy) UNMutableNotificationContent *bestAttemptContent;


@implementation NotificationService

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    // Modify the notification content material right here...
    //self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];
    [[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent withContentHandler:contentHandler];

- (void)serviceExtensionTimeWillExpire {
    // Referred to as simply earlier than the extension shall be terminated by the system.
    // Use this as a chance to ship your "greatest try" at modified content material, in any other case the unique push payload shall be used.


Firebase dependencies:

  firebase_messaging: ^14.7.9
  firebase_core: ^2.24.2
  flutter_local_notifications: ^16.2.0
  firebase_analytics: ^10.7.4

Leave a Reply

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