firebase / flutterfire

πŸ”₯ A collection of Firebase plugins for Flutter apps.
https://firebase.google.com/docs/flutter/setup
BSD 3-Clause "New" or "Revised" License
8.51k stars 3.92k forks source link

firebase_messaging: Normal Push Notifications appear silently or not at all when the flutter app is terminated #2603

Closed royibernthal closed 3 years ago

royibernthal commented 4 years ago

My flutter doctor is perfect.

I'm using firebase-admin on nodejs to send push notifications to users: https://firebase.google.com/docs/admin/setup

I'm using firebase_messaging 6.0.13 on flutter: https://pub.dev/packages/firebase_messaging

I'm trying to send a push notification with the highest priority on both android and ios.

    firebase.messaging(firebaseApp).send({
        token,
        notification,
        android: { priority: 'high' },
        apns: { headers: { 'apns-priority': '10' }, payload: { aps: { sound: 'default' } } },
        data
    }

On my development devices it works like a charm both when the app is in the background and when it's terminated.

I'm receiving push notifications that visibly pop and make a sound 100% of the time, on both android and ios.

The issue is on other devices -

If the app is in the background, I can sometimes see an actual push notification visibly pop, and make a sound as it should. Sometimes not.

If the app is terminated, a push notification is received - but it doesn't visibly pop nor make a sound, I can only see it if I scroll down the menu from the top of the screen, it's listed as a received push notification. Sometimes the notification is not receieved at all.

I tried to change the priority of notifications from my app to high on the actual device as well, but it didn't change anything.

My users keep complaining that they're not receiving push notifications - I'm guessing they either actually don't receieve them or simply don't see them since they're recieved silently as I described above.

I literally have no idea what's going on and why it doesn't work as it should on devices other than my own development devices.

Any ideas?

TahaTesser commented 4 years ago

Hi @royibernthal Is the issue on other devices are just android or both iOS and Android? can you please provide your flutter doctor -v and flutter run --verbose? Can you please your MainActivity and build.gradle? Also, to better address the issue, would be helpful if you could post a minimal code sample to reproduce the problem Thank you

royibernthal commented 4 years ago

Hi @TahaTesser

The issue is on both iOS and Android.

flutter doctor -v on the machine that compiles for android

[√] Flutter (Channel stable, v1.17.1, on Microsoft Windows [Version 10.0.18363.836], locale en-US)
    β€’ Flutter version 1.17.1 at D:\flutter
    β€’ Framework revision f7a6a7906b (9 days ago), 2020-05-12 18:39:00 -0700
    β€’ Engine revision 6bc433c6b6
    β€’ Dart version 2.8.2

[√] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
    β€’ Android SDK at C:\Users\royib\AppData\Local\Android\Sdk
    β€’ Platform android-29, build-tools 29.0.3
    β€’ ANDROID_HOME = C:\Users\royib\AppData\Local\Android\Sdk
    β€’ Java binary at: D:\Program Files\Android\Android Studio\jre\bin\java
    β€’ Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b04)
    β€’ All Android licenses accepted.

[√] Android Studio (version 3.6)
    β€’ Android Studio at D:\Program Files\Android\Android Studio
    β€’ Flutter plugin version 45.1.1
    β€’ Dart plugin version 192.8052
    β€’ Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b04)

I have a different machine for compiling ios, I can provide flutter doctor for that a bit later. There are no errors there though, is there other info you'd like to see there?

flutter run --verbose produces a very long log that gets cut in the terminal, is there any way for me to get the full log?

MainActivity.kt

package com.lumney

import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant

class MainActivity: FlutterActivity() {
    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine);
    }
}

android/build.gradle

buildscript {
    ext.kotlin_version = '1.3.50'
    repositories {
        google()
        jcenter()
        maven {
            url 'https://maven.fabric.io/public'
        }
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.google.gms:google-services:4.3.2'
        classpath 'io.fabric.tools:gradle:1.26.1'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

rootProject.buildDir = '../build'
subprojects {
    project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
    project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

android/app/build.gradle

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
apply plugin: 'io.fabric'
apply plugin: 'com.google.gms.google-services'

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    compileSdkVersion 28

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    lintOptions {
        disable 'InvalidPackage'
    }

    defaultConfig {
        applicationId "com.lumney"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}

flutter {
    source '../..'
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.google.firebase:firebase-messaging:20.1.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

I'm not sure how I could post a minimal code sample to reproduce the problem without creating a separate app on both flutter and firebase and testing it on many devices, and that'd be a huge task. Correct me if I'm wrong.

4tKnight commented 4 years ago

Same issue I am having currently

corvus89 commented 4 years ago

I have got the same problem. Push notifications show instantly on one iOS device (iPhone 5c, 10.3.3) and not at all if app is in background/terminated on other (iPhone 6S, 13.5). App was installed from the same archive through diawi in debug mode. On iPhone 6S onMessage() is called if app is in foreground or after resuming app.

It works fine both foreground and background on any Android I tested it on.

I won't be able to post flutter run --verbose because I'm working on commercial product. There is too much information reviled there and it is too long to anonymize it. If some can publish that kind of logs you can run flutter run --verbose | tee log.txt. It will save copy of console printed text to file.

[βœ“] Flutter (Channel stable, v1.17.1, on Mac OS X 10.15.4 19E287, locale en-GB)
    β€’ Flutter version 1.17.1 at /Users/X/flutter
    β€’ Framework revision f7a6a7906b (13 days ago), 2020-05-12 18:39:00 -0700
    β€’ Engine revision 6bc433c6b6
    β€’ Dart version 2.8.2

[βœ“] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
    β€’ Android SDK at /Users/X/Library/Android/sdk
    β€’ Platform android-R, build-tools 29.0.3
    β€’ ANDROID_HOME = /Users/X/Library/Android/sdk
    β€’ Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    β€’ Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
    β€’ All Android licenses accepted.

[βœ“] Xcode - develop for iOS and macOS (Xcode 11.5)
    β€’ Xcode at /Applications/Xcode.app/Contents/Developer
    β€’ Xcode 11.5, Build version 11E608c
    β€’ CocoaPods version 1.8.4

[βœ“] Android Studio (version 3.6)
    β€’ Android Studio at /Applications/Android Studio.app/Contents
    β€’ Flutter plugin version 45.1.1
    β€’ Dart plugin version 192.7761
    β€’ Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
samueladekunle commented 4 years ago

I am also experiencing the same issue on Android. When the app is in the foreground, notifications are handled in the onMessage handler. When the app is in the background, notifications appear in the system tray. But when the app is killed/terminated, nothing happens.

wladrbarbosa commented 4 years ago

Try to remove all notification attributes from the payload of send requests and use only the data attribute with default information like title and body. Maybe this will work. Something like this:

HTTP v1 version

{ "message": { "token": "DEVICE_TOKEN", "data": { "title": "title", "body": "body", "image": "IMAGE_URL", "click_action" : "FLUTTER_NOTIFICATION_CLICK" }, "android": { "priority" : "HIGH" }, "apns": { "headers": { "apns-priority": "10" } } } }

Legacy version

{ "to" : "DEVICE_TOKEN", "priority" : "high", "content_available" : true, "data" : { "body" : "body", "title": "title", "click_action" : "FLUTTER_NOTIFICATION_CLICK", "image" : "IMAGE_URL" } }

royibernthal commented 4 years ago

Try to remove all notification attributes from the push send and use only the data attribute with default information like title and body. Maybe this will work. Something like this:

HTTP v1 version

{ "message": { "token": "DEVICE_TOKEN", "data": { "title": "title", "body": "body", "image": "IMAGE_URL", "click_action" : "FLUTTER_NOTIFICATION_CLICK" }, "android": { "priority" : "HIGH" }, "apns": { "headers": { "apns-priority": "10" } } } }

Legacy version

{ "to" : "DEVICE_TOKEN", "priority" : "high", "content_available" : true, "data" : { "body" : "body", "title": "title", "click_action" : "FLUTTER_NOTIFICATION_CLICK", "image" : "IMAGE_URL" } }

Wouldn't omitting the notification key result in a data-only notification which by definition will not be visually displayed to the user?

Have you tried that?

wladrbarbosa commented 4 years ago

Yes, here is working. After remove the notification attribute you can use the plugin flutter_local_notifications to show a big picture notification in place, for example. But I haven't tested this on iOS.

royibernthal commented 4 years ago

Are you calling flutter_local_notifications in firebase_messaging's onBackgroundMessage? If so, from my previous experience with it there are many problems with it, so I'm a bit hesitant. If not, how do you use flutter_local_notifications when the app is terminated?

Also, shouldn't firebase_messaging just work on its own out of the box? I feel like it shouldn't be this complicated to receive push notifications.

wladrbarbosa commented 4 years ago

Here is my files:

NotificationsHandler.dart

import 'dart:async';
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:http/http.dart';
import 'package:rxdart/rxdart.dart';
import 'package:path_provider/path_provider.dart';

// Streams are created so that app can respond to notification-related events since the plugin is initialised in the `main` function
final BehaviorSubject<ReceivedNotification> didReceiveLocalNotificationSubject =
    BehaviorSubject<ReceivedNotification>();

final BehaviorSubject<String> selectNotificationSubject =
    BehaviorSubject<String>();

class ReceivedNotification {
  final int id;
  final String title;
  final String body;
  final String payload;

  ReceivedNotification({
    @required this.id,
    @required this.title,
    @required this.body,
    @required this.payload,
  });
}

class SecondScreen extends StatefulWidget {
  SecondScreen(this.payload);

  final String payload;

  @override
  State<StatefulWidget> createState() => SecondScreenState();
}

class SecondScreenState extends State<SecondScreen> {
  String _payload;
  @override
  void initState() {
    super.initState();
    _payload = widget.payload;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('test')),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text('${(_payload ?? '')} Go back!'),
        ),
      ),
    );
  }
}

class NotificationsHandler {
  void _configureDidReceiveLocalNotificationSubject(BuildContext context) {
    didReceiveLocalNotificationSubject.stream
        .listen((ReceivedNotification receivedNotification) async {
      await showDialog(
        context: context,
        builder: (BuildContext contex2) => AlertDialog(
          title: receivedNotification.title != null
              ? Text(receivedNotification.title)
              : null,
          content: receivedNotification.body != null
              ? Text(receivedNotification.body)
              : null,
          actions: [
            FlatButton(
              child: Text('Ok'),
              onPressed: () async {
                Navigator.of(context, rootNavigator: true).pop();
                await Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context2) =>
                        SecondScreen(receivedNotification.payload),
                  ),
                );
              },
            )
          ],
        ),
      );
    });
  }

  void _configureSelectNotificationSubject(BuildContext context) {
    selectNotificationSubject.stream.listen((String payload) async {
      await Navigator.push(
        context,
        MaterialPageRoute(builder: (context2) => SecondScreen(payload)),
      );
    });
  }

  NotificationAppLaunchDetails notificationAppLaunchDetails;
  FirebaseMessaging fcm = FirebaseMessaging();
  StreamSubscription iosSubscription;
  NotificationsHandler();

  initializeFcmNotification(BuildContext context) async {
    _configureDidReceiveLocalNotificationSubject(context);
    _configureSelectNotificationSubject(context);
    fcm.configure(
      onMessage: (Map<String, dynamic> message) async {
        debugPrint("[initializeFcmNotification] onMessage: $message");
        message['data']['callback'] = 'onMessage';
        _showBigTextNotification(message);
      },
      onBackgroundMessage: Platform.isIOS ? null : _myBackgroundMessageHandler,
      onLaunch: (Map<String, dynamic> message) async {
        debugPrint("[initializeFcmNotification] onLaunch: $message");
        await Navigator.push(
          HERE_I_USED_A_STATIC_BUILDCONTEXT_VAR,
          MaterialPageRoute(builder: (context2) => SecondScreen('onLaunch')),
        );
      },
      onResume: (Map<String, dynamic> message) async {
        debugPrint("[initializeFcmNotification] onResume: $message");
        await Navigator.push(
          HERE_I_USED_A_STATIC_BUILDCONTEXT_VAR,
          MaterialPageRoute(builder: (context2) => SecondScreen('onResume')),
        );
      },
    );
    var initializationSettingsAndroid = AndroidInitializationSettings('ic_launcher_foreground');
    // Note: permissions aren't requested here just to demonstrate that can be done later using the `requestPermissions()` method
    // of the `IOSFlutterLocalNotificationsPlugin` class
    var initializationSettingsIOS = IOSInitializationSettings(
        onDidReceiveLocalNotification:
            (int id, String title, String body, String payload) async {
          didReceiveLocalNotificationSubject.add(ReceivedNotification(
              id: id, title: title, body: body, payload: payload));
        });
    var initializationSettings = InitializationSettings(
        initializationSettingsAndroid, initializationSettingsIOS);
    await FlutterLocalNotificationsPlugin().initialize(initializationSettings,
        onSelectNotification: (String payload) async {
      if (payload != null) {
        debugPrint('notification payload: ' + payload);
      }
      selectNotificationSubject.add(payload);
    });

    if (Platform.isIOS) {
      iosSubscription = fcm.onIosSettingsRegistered.listen((data) {
        // save the token  OR subscribe to a topic here
      });

      fcm.requestNotificationPermissions(IosNotificationSettings());
    } else {
      _saveDeviceToken();
    }
  }

  static Future<dynamic> _myBackgroundMessageHandler(Map<String, dynamic> message) async {
    debugPrint("[myBackgroundMessageHandler] onBackgroundMessage: $message");
    message['data']['callback'] = 'onBackgroundMessage';
    _showBigTextNotification(message);
    // Or do other work.
    return Future<void>.value();
  }

  static Future<String> _downloadAndSaveFile(String url, String fileName) async {
    Directory directory = await getTemporaryDirectory();
    var filePath = '${directory.path}/$fileName';
    var response = await get(url);
    var file = File(filePath);
    await file.writeAsBytes(response.bodyBytes);
    return filePath;
  }

  static Future<void> _showBigTextNotification(Map<String, dynamic> message) async {
    var largeIconPath = await _downloadAndSaveFile(
        message['data']['image'], 'largeIcon');
    var bigTextStyleInformation = BigTextStyleInformation(
      '${message['data']['body']}',
      htmlFormatBigText: true,
      contentTitle: '${message['data']['title']}',
      htmlFormatContentTitle: true,
      //summaryText: '$summary',
    );
    var androidPlatformChannelSpecifics = AndroidNotificationDetails(
        '0',
        '${message['data']['title']}',
        '${message['data']['body']}',
        enableLights: true,
        color: Color(int.parse(message['data']['mainColor'], radix: 16) + 0xFF000000),
        largeIcon: FilePathAndroidBitmap(largeIconPath),
        ledOnMs: 1000,
        ledOffMs: 500,
        importance: Importance.High,
        priority: Priority.High,
        ledColor: Color(int.parse(message['data']['secondaryColor'], radix: 16)),
        ticker: 'ticker',
        styleInformation: bigTextStyleInformation,
    );
    var iOSPlatformChannelSpecifics =
      IOSNotificationDetails(attachments: [IOSNotificationAttachment(largeIconPath)]);
    var platformChannelSpecifics =
        NotificationDetails(androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
    await FlutterLocalNotificationsPlugin().show(
        0, '${message['data']['title']}', '${message['data']['body']}', platformChannelSpecifics, payload: message['data']['callback']);
  }

  /// Get the token, save it to the database for current user
  _saveDeviceToken() async {
    debugPrint("[_saveDeviceToken] FCM_TOKEN: ${await fcm.getToken()}");
  }

  Future<void> onDidReceiveLocalNotification(
      int id, String title, String body, String payload) async {
    // display a dialog with the notification details, tap ok to go to another page
  }
}

Application.java

package PACKAGE_NAME;

import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService;

public class Application extends FlutterApplication implements PluginRegistrantCallback {
  @Override
  public void onCreate() {
    super.onCreate();
    FlutterFirebaseMessagingService.setPluginRegistrant(this);
  }

  @Override
  public void registerWith(PluginRegistry registry) {
    FirebaseCloudMessagingPluginRegistrant.registerWith(registry);
  }
}

FirebaseCloudMessagingPluginRegistrant.java

package PACKAGE_NAME;

import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin;
import io.flutter.plugins.pathprovider.PathProviderPlugin;
import com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin;

public final class FirebaseCloudMessagingPluginRegistrant{
  public static void registerWith(PluginRegistry registry) {
    if (alreadyRegisteredWith(registry)) {
      return;
    }
    io.flutter.plugins.pathprovider.PathProviderPlugin.registerWith(registry.registrarFor("io.flutter.plugins.pathprovider.PathProviderPlugin"));
    com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.registerWith(registry.registrarFor("com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin"));
    FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
  }

  private static boolean alreadyRegisteredWith(PluginRegistry registry) {
    final String key = FirebaseCloudMessagingPluginRegistrant.class.getCanonicalName();
    if (registry.hasPlugin(key)) {
      return true;
    }
    registry.registrarFor(key);
    return false;
  }
}
royibernthal commented 4 years ago

Got it. So yeah, you're doing exactly that :) You're calling flutter_local_notifications in firebase_messaging's onBackgroundMessage. I suppose it wouldn't even work on IOS due to the simple fact that onBackgroundMessage is not supported on IOS. Let's even pretend for a moment that it doesn't have issues on Android.

AhmedNourJamalElDin commented 4 years ago

Any update?

royibernthal commented 4 years ago

Still researching and testing this to no avail. I'm hoping one of the plugin creators / moderators will step in at some point.

AhmedNourJamalElDin commented 4 years ago

Any update from the team, please? Push notifications without this feature is almost nothing.

braysonjohn148 commented 3 years ago

having the same issue. I also tried pushing notifications from firebase console(FCM) but cannot get the notifications when the app is terminated.

royibernthal commented 3 years ago

On android you have to make sure you're setting up notification channels on the device and then sending push notifications to those channels, either from your backend or frontend (though frontend is not a good practice). firebase_messaging package for flutter doesn't take care of that for you.

I personally decided to move the whole project to OneSignal, it's free and it handles everything for me without any headaches, plus normal customer support. Since I moved to OneSignal I no longer have any issues with push notifications. Feels like I'm writing an ad, but I guess they deserve it, I'm a satisfied customer.

braysonjohn148 commented 3 years ago

moving the whole project now could be very difficult for me. On the tutorials i never seen anything about setting up notification channels on the device. I believe its something with my code.

royibernthal commented 3 years ago

The migration was actually very easy.

Well, that's a big part of the problem, I had to dig deep in order to understand what was wrong.

Maybe something is wrong with your code as you say, but there's a good chance that your code is okay and you're just missing this piece of the puzzle, which is hardly mentioned anywhere for some reason.

Anyway, these are my 2 cents as someone who's been struggling with this for a long time :)

braysonjohn148 commented 3 years ago

When app is in foreground, and background i receive the notifications perfectly. But when the app is completely terminated i don't.

Cannot be with data or notification because i am trying using the Firebase Console: Cloud Messaging, Compose notification, but only when the app is foreground or in stacks not terminated.

I followed all instructions from the firebase read me and some tutorials.

Android Manifest: `

    <meta-data android:name="com.google.firebase.messaging.default_notification_channel_id"
               android:value="@string/default_notification_channel_id"/>`

android/app/build.gradle implementation 'com.google.firebase:firebase-messaging:20.2.4'

android/build.gradle classpath 'com.android.tools.build:gradle:3.5.3' classpath 'com.google.gms:google-services:4.3.3' Pubs firebase_messaging: ^6.0.16

When i push the notification when the app is in background (not terminated but in stack). I get a message: Missing Default Notification Channel metadata in AndroidManifest. Default value will be used

Flutter doctor: Run flutter doctor and paste the output below:

[βœ“] Flutter (Channel stable, v1.17.5, on Mac OS X 10.14.6 18G103, locale en-TZ) β€’ Flutter version 1.17.5 at /Users/mac/Documents/FlutterSDK/flutter β€’ Framework revision 8af6b2f038 (5 weeks ago), 2020-06-30 12:53:55 -0700 β€’ Engine revision ee76268252 β€’ Dart version 2.8.4

[βœ“] Android toolchain - develop for Android devices (Android SDK version 29.0.3) β€’ Android SDK at /Users/mac/Library/Android/sdk β€’ Platform android-29, build-tools 29.0.3 β€’ Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java β€’ Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405) β€’ All Android licenses accepted.

[βœ“] Xcode - develop for iOS and macOS (Xcode 11.3.1) β€’ Xcode at /Applications/Xcode.app/Contents/Developer β€’ Xcode 11.3.1, Build version 11C505 β€’ CocoaPods version 1.8.4

[βœ“] Android Studio (version 3.5) β€’ Android Studio at /Applications/Android Studio.app/Contents β€’ Flutter plugin version 43.0.1 β€’ Dart plugin version 191.8593 β€’ Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)

[βœ“] VS Code (version 1.46.1) β€’ VS Code at /Applications/Visual Studio Code.app/Contents β€’ Flutter extension version 3.12.2

[βœ“] Connected device (1 available) β€’ TECNO KB7j β€’ 044033396I137857 β€’ android-arm β€’ Android 9 (API 28)

β€’ No issues found!

markg85 commented 3 years ago

@braysonjohn148 I'm having exactly the same issue. Did you manage to solve your issue? If so, i'd be really eager to know how if you mind sharing!

AhmedNourJamalElDin commented 3 years ago

Hi all, I managed to solve the issue with no exceptions in the devices I tried it on.

The problem related to Android is that I had a device that doesn't allow notifications to work in the background, and it's solved by turning on some options in the device itself. the option is Allow running in background.

The problems related to iOS is that for some reason I needed to add rows to ios/Runner.xcodeproj/project.pbxproj file related to Runner.entitlements manually because I don't have macOS.

Up to now, everything works fine. but this doesn't mean I solved the issue for all devices because, as you know, I don't have all available devices to test on.

I hope it gets fixed for you guys.

Thanks

markg85 commented 3 years ago

The problem related to Android is that I had a device that doesn't allow notifications to work in the background, and it's solved by turning on some options in the device itself. the option is Allow running in background.

That's great, thank you for sharing! :)

But.... going the Allow running in background does mean your app will consume (much more) battery as it keeps running. So perhaps this route is not advisable.

AhmedNourJamalElDin commented 3 years ago

Yea, it's true. But the phone doesn't receive any notification neither from my app nor from other apps.

I'd like to know if there is any other possible solution.

SamerOrfali commented 3 years ago

same issue here ! any update ?

NightOwlCoder commented 3 years ago

Guys, I'm ashamed to say this, but for me, my test Droid device was running under "no disturb mode", so everything was just going to control center, no sound, no vibration, no pop ups. As soon as I turned it off, all started working as expected. 🀦

shashikantdurge commented 3 years ago

This worked for me! :

AndroidManifest.xml

 <application
        android:name=".App"                   //ADD THIS LINE
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name">

App.kt

package com.leher
import io.flutter.app.FlutterApplication

///This is used in AndroidManifest.xml
class App : FlutterApplication() {
    override fun onCreate() {
        super.onCreate()
        //XYZ
    }
}
kjawadDeveloper commented 3 years ago

Any luck?

zaher-recode commented 3 years ago

Not sure if this is the same but throwing this in to help others who have the same issue , for me I had the notification shows onMessage and onResume, however the onLaunch only shows notification in control, tapping on that opens the app only when terminated, the issue for me was the app needs time to run with the splash screen which prevented the app from running the notification correctly in app: this helped me:

import 'package:flutter/scheduler.dart';

// call back on background message
  onBackgroundMessage:
      Platform.isAndroid ? myBackgroundMessageHandler : null,

  onLaunch: (Map<String, dynamic> msg) async {

    // give time for splashscreen to run and finish before it's done.
    SchedulerBinding.instance
        .addPostFrameCallback((_) => _showNotification(msg));
  },
jcsena commented 3 years ago

same issue here ! any update ? some workaround ?

jcsena commented 3 years ago

Finally, I found a workaround for notification when the app is killed or terminated

1). I just added a new file called App.java inside the folder java/[com]/[yourdomain]

 package [com].[yourdomain];
 import io.flutter.app.FlutterApplication;

 public class App extends FlutterApplication {
   @Override
   public void onCreate() {
     super.onCreate();
   }
 }

2) Then, I added the android:name=".App" inside my AndroidManifest.xml file.

<application
        android:name=".App"
       ...
 >

Run the application again and the notifications working correctly with the app terminated or killed

I hope this can help you too.

Naveed-dev30 commented 3 years ago

OneSignal

I also implemented that. But still got the problem of not receiving notification when app was killed completely

lulupointu commented 3 years ago

Issue still appearing on firebase_messaging 8.0.0-dev.8. Only on Android though.

wangxingxing123654 commented 3 years ago

I am also experiencing the same issue on Android. When the app is in the foreground, notifications are handled in the onMessage handler. When the app is in the background, notifications appear in the system tray. But when the app is killed/terminated, nothing happens.

any update???

braysonjohn148 commented 3 years ago

Finally, I found a workaround for notification when the app is killed or terminated

1). I just added a new file called App.java inside the folder java/[com]/[yourdomain]

 package [com].[yourdomain];
 import io.flutter.app.FlutterApplication;

 public class App extends FlutterApplication {
   @Override
   public void onCreate() {
     super.onCreate();
   }
 }
  1. Then, I added the android:name=".App" inside my AndroidManifest.xml file.
<application
        android:name=".App"
       ...
 >

Run the application again and the notifications working correctly with the app terminated or killed

I hope this can help you too.

I Really can't see how this could solve anything here.

Naveed-dev30 commented 3 years ago

So after 1year of continuous searching for the solution to that problem, researched al lot, talked to many developers. It has NO solution eventually. It totally depends on the manufacturer of mobile phone. How ever if any one knows how telegram, facebook and whatsapp is doing to send notifications please let us know. Thank you.

lulupointu commented 3 years ago

I found the solution for my problem, which was: notifications appear silently ON ANDROID when the app is in the background or terminated. The problem appeared on every phone (==2) I had and the fix solves the issue for both phones. Beware that I use firebase_messaging: 8.0.0-dev.11.

The key solution can be found here. I don't know why I didn't try that before, maybe is this a new documentation page, but I never came across this section in any tutorial/fix. In any case I don't see why this is burred under "advance usage" while I think displaying the incoming notifications when the app is in the background or terminated should be the default behaviour.

I will copy the instructions and a small reproducible example here in case the link brakes.

Instructions

The main idea is the following: > On Android, notification messages are sent to Notification Channels which are used to control how a notification is delivered. **The default FCM channel** used is hidden from users, however **provides a "default" importance level**. **Heads up notifications require a "max" importance level**. So we need to create a high importance channel through the [flutter_local_notifications](url) package. To do so we: 1. Add the flutter_local_notifications package to your local project. 2. Create a new AndroidNotificationChannel instance: ```dart const AndroidNotificationChannel channel = AndroidNotificationChannel( 'high_importance_channel', // id 'High Importance Notifications', // title 'This channel is used for important notifications.', // description importance: Importance.max, ); ``` 3. Create the channel on the device (if a channel with an id already exists, it will be updated): ```dart final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation() ?.createNotificationChannel(channel); ``` Once created, we can now update FCM to use our own channel rather than the default FCM one. To do this, open the android/app/src/main/AndroidManifest.xml file for your FlutterProject project. Add the following meta-data schema within the application component: ```xml ```

Reproductible example

**main.dart** ```dart 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'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MyApp()); FirebaseMessaging messaging = FirebaseMessaging.instance; print(await messaging.getToken()); const AndroidNotificationChannel channel = AndroidNotificationChannel( 'high_importance_channel', // id 'High Importance Notifications', // title 'This channel is used for important notifications.', // description importance: Importance.max, ); final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation() ?.createNotificationChannel(channel); FirebaseMessaging.onMessage.listen((RemoteMessage message) { print('Got a message whilst in the foreground!'); print('Message data: ${message.data}'); if (message.notification != null) { print('Message also contained a notification: ${message.notification}'); } }); FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); } Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { print("Handling a background message: ${message.messageId}"); } Future selectNotification(String payload) { print("selectNotification. payload: $payload"); } Future onDidReceiveLocalNotification(int id, String title, String body, String payload) { print("onDidReceiveLocalNotification. payload: $payload"); } class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Title'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. ); } } ``` pubspec.yaml ```yaml name: flutter_test_fcm description: A new Flutter application. publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: sdk: ">=2.7.0 <3.0.0" dependencies: flutter: sdk: flutter firebase_core: "^0.5.3" firebase_messaging: "^8.0.0-dev.11" flutter_local_notifications: ^3.0.2 dev_dependencies: flutter_test: sdk: flutter flutter: uses-material-design: true ``` **AndroidManifest.xml** ```xml ``` Beware that you also need to initialize firebase and place the google-services.json in /android/app If you use flutterEmbedding v2 no need to do anything with the Application.java, otherwise refer to the documentation (not tested however)

Hopes this helps you as much as this helped me, I was searching for a solution for 6 month!

CoderJava commented 3 years ago

Finally I fixed it with update plugin version firebase_messaging to 8.0.0-dev.10. Here is my body FCM.

{
    "content_available": true,
    "data": {
        "key1": "key1"
    },
    "to": "d-eBMw_AYkAwuTA9_dEAwy:APA91bHc7ApE7FXf0w6zdrWdhUA0-pSIR79L0G7_WlJl_U0LKudj6MpDWSoODqk-vS5FEsFY-obTDyPabIeaczRAeoFCJY4XOQTq-IM1iwIdYE45J_rK2XW9R0kztDgFFys97fOU79yg"
}

It's working in background and terminated mode on iOS. I'm just following step by step in firebase.flutter.dev. https://firebase.flutter.dev/docs/messaging/usage

In this video I'm used flutter_local_notification to show local notification when received data from FCM. https://youtu.be/q7OsFbsvJos

I'm tested on real device iPhone 6S iOS 14.

Hopefully it's working for all of you.

flutter doctor -v

[βœ“] Flutter (Channel beta, 1.24.0-10.2.pre, on Mac OS X 10.15.7 19H2 darwin-x64, locale en-EC)
    β€’ Flutter version 1.24.0-10.2.pre at /Users/yudisetiawan/fvm/versions/beta
    β€’ Framework revision 022b333a08 (4 weeks ago), 2020-11-18 11:35:09 -0800
    β€’ Engine revision 07c1eed46b
    β€’ Dart version 2.12.0 (build 2.12.0-29.10.beta)

[βœ“] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
    β€’ Android SDK at /Users/yudisetiawan/Library/Android/sdk
    β€’ Platform android-30, build-tools 30.0.2
    β€’ Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    β€’ Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
    β€’ All Android licenses accepted.

[βœ“] Xcode - develop for iOS and macOS (Xcode 12.0.1)
    β€’ Xcode at /Applications/Xcode.app/Contents/Developer
    β€’ Xcode 12.0.1, Build version 12A7300
    β€’ CocoaPods version 1.10.0

[βœ“] Chrome - develop for the web
    β€’ Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[βœ“] Android Studio (version 4.1)
    β€’ Android Studio at /Applications/Android Studio.app/Contents
    β€’ Flutter plugin can be installed from:
      πŸ”¨ https://plugins.jetbrains.com/plugin/9212-flutter
    β€’ Dart plugin can be installed from:
      πŸ”¨ https://plugins.jetbrains.com/plugin/6351-dart
    β€’ Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)

[βœ“] IntelliJ IDEA Community Edition (version 2020.2.3)
    β€’ IntelliJ at /Applications/IntelliJ IDEA CE.app
    β€’ Flutter plugin can be installed from:
      πŸ”¨ https://plugins.jetbrains.com/plugin/9212-flutter
    β€’ Dart plugin can be installed from:
      πŸ”¨ https://plugins.jetbrains.com/plugin/6351-dart

[βœ“] VS Code (version 1.50.0)
    β€’ VS Code at /Applications/Visual Studio Code.app/Contents
    β€’ Flutter extension version 3.16.0

[βœ“] Connected device (3 available)
    β€’ Yudi’s iPhone (mobile) β€’ 1b7890540306c7d8155bacffabc03043d4c28bf9 β€’ ios            β€’ iOS 14.0.1
    β€’ Web Server (web)       β€’ web-server                               β€’ web-javascript β€’ Flutter Tools
    β€’ Chrome (web)           β€’ chrome                                   β€’ web-javascript β€’ Google Chrome 87.0.4280.88

β€’ No issues found!

pubspec.yaml

name: isapp
description: Aplikasi Nusawork.

# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.6.0+19 # Android
# version: 0.6.0+13 # IOS

environment:
  sdk: ">=2.7.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.3

  # A powerful Http client for Dart, which supports  interceptors, FormData, Request Cancellation,
  # File Downloading, Timeout etc.
  dio: ^3.0.10

  # Flutter widgets that make it easy to implement the BLoC design pattern.
  flutter_bloc: ^4.0.0

  # Package provides internationalized and localized messages, including message translation,
  # plurals and genders, date/number formatting and parsing, and bidirectional text.
  intl: ^0.16.0

  # Flutter library to load and cache network images. Can also be used with placeholder and
  # error widgets.
  cached_network_image: ^2.2.0+1

  # Simple direct Service Locator that allows to decouple the interface from a concrete
  # implementation and to access the concrete implementation from everywhere in your app.
  get_it: ^4.0.1

  # Functional Programming in Dart. Purify your Dart code using efficient immutable data
  # structures monads, lenses and other FP tools.
  dartz: ^0.8.9

  # An abstract class that helps to implement equality without needing to explicitly
  # override == and hashCode.
  equatable: ^1.1.1

  # A pure Dart library that checks for internet by opening a socket to a list of specified
  # addresses, each with individual port and timeout.
  data_connection_checker: ^0.3.4

  # A Flutter plugin for adapting screen and font size.
  flutter_screenutil: ^1.1.0

  # Flutter plugin providing detailed information about the device (make, model, etc) and
  # Android or OS version the app is running on.
  device_info: ^0.4.2+1

  # Flutter plugin for reading and writing key-value pairs. Wraps NSUserDefaults on iOS and
  # SharedPreferences on Android.
  shared_preferences: ^0.5.7

  # The Font Awesome Icon pack available as Flutter Icons.
  font_awesome_flutter: ^8.8.1

  # Permission plugin for Flutter.
  permission_handler: ^5.0.0+hotfix.6

  # A Flutter plugin for integrating Google Maps in IOS and Android.
  google_maps_flutter: ^1.0.6

  # Geolocation plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API
  # for generic location (GPS etc.) functions.
  geolocator: ^6.1.13

  # A Flutter Geocoding plugin which provides easy geocoding and reverse-geocoding features.
  geocoding: ^1.0.5

  # A Flutter plugin for getting information about and controlling the camera on
  camera: ^0.5.8+2

  # A string-based path manipulation library.
  path: ^1.6.4

  # Flutter plugin for getting commonly used locations on the Android & iOS file systems,
  # such as the temp and app data directories.
  path_provider: 1.1.0

  # An SVG rendering and widget library for Flutter.
  flutter_svg: ^0.18.0

  # A Flutter package to let users easily add a dashed border around any widget.
  dotted_border: ^1.0.5

  # Flutter plugin for selecting images from the Android and IOS image library, and taking new
  # pictures with the camera.
  image_picker: ^0.6.7+4

  # Flutter plugin for Firebase Cloud Messaging.
  firebase_messaging: ^8.0.0-dev.10

  # Flutter plugin for querying information about the application package,
  # such as CFBundleVersion on iOS or versionCode on Android.
  package_info: ^0.4.1

  # A cross platform plugin for displaying and scheduling local notifications for Flutter
  # applications with the ability to customize for each platform.
  flutter_local_notifications: ^3.0.1+6

  # A package provides an easy way to add shimmer effect in Flutter.
  shimmer: ^1.1.1

  # RxDart is an implementation of the popular reactiveX api for asynchronous programming,
  # leveraging the native Dart Streams  api.
  rxdart: ^0.24.1

  # Photo View provides a gesture sensitive zoomable widget.
  photo_view: ^0.10.2

  # Easy and fast internationalizing and localization your Flutter Apps.
  easy_localization: ^2.3.3

  # A styled Toast Flutter package.
  flutter_styled_toast: ^1.4.0+1

  # A Flutter plugin for Android and iOS supports cropping images.
  image_cropper: ^1.3.1

  # Flutter plugin for Firebase Crashlytics.
  firebase_crashlytics: ^0.2.3+1

  # Flutter plugin for Firebase Core, enabling connection to muliple Firebase apps.
  firebase_core: ^0.5.3

  # Flutter plugin for launching a URL on Android & iOS. Supports web, phone, SMS, and
  # email schemes.
  url_launcher: ^5.7.4

  # Time zone database and time zone aware DateTime.
  timezone: ^0.5.7

  # A plug-in that can call native APP to open files with string result in flutter,
  # support iOS (UTI) / android (intent) / PC (ffi) / web (dart:html)
  open_file: ^3.0.3

  # The typesafe, reactive and lightweight SQLite abstraction
  floor: ^0.17.0

  # Enables In App Updates on Android using the official Android APIs.
  in_app_update: ^1.1.14

  # Provides a simple class for parsing and comparing semantic versions as defined
  # by http://semver.org/
  version: ^1.2.0

  # A Flutter plugin for getting the local timezone of the OS.
  flutter_native_timezone: ^1.0.4

  # Flutter plugin for accessing the Android AlarmManager service, and running
  # Dart code in the background when alarms fire.
  android_alarm_manager: ^0.4.5+7

  # Flutter plugin for showing the In-App Review/System Rating pop up on Android,
  # IOS and MacOS. Makes it easy for users to rate your app.
  in_app_review: ^1.0.4

  # A simple plugin to take screenshots using native code (iOS & Android).
  native_screenshot: ^0.0.4

  # Compress image with native code. It's faster. This library can work on Android/iOS.
  flutter_image_compress: ^0.7.0

dependency_overrides:
  path_provider: 1.1.0

dev_dependencies:
  flutter_test:
    sdk: flutter

  # Tools to write binaries that run builders.
  build_runner: ^1.10.3

  # A mock framework inspired by Mockito.
  mockito: ^4.1.1

  # A testing library which make it easy to test blocs.
  bloc_test: ^5.0.0

  # Automatically generate code for converting to and from JSON by annotating Dart classes.
  json_serializable: ^3.3.0

  # How to get the most value from Dart static analysis.
  pedantic: ^1.9.0

  # The typesafe, reactive and lightweight SQLite abstraction
  floor_generator: ^0.17.0

# The following section is specific to Flutter.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  assets:
    - assets/image/
    - assets/svg/
    - assets/translations/

  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.dev/assets-and-images/#resolution-aware.

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/assets-and-images/#from-packages

  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.dev/custom-fonts/#from-packages

main.dart

import 'dart:io';
import 'dart:isolate';

import 'package:android_alarm_manager/android_alarm_manager.dart';
import 'package:easy_localization/easy_localization.dart';
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 'package:isapp/core/util/method_channel_helper.dart';

import 'app.dart';
import 'config/base_url_config.dart';
import 'config/flavor_config.dart';
import 'core/util/notification_handler.dart';
import 'core/util/shared_preferences_manager.dart';
import 'injection_container.dart' as di;

Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  debugPrint('Handling a background message: ${message.messageId}');
  debugPrint('Background message contained data: ${message.data}');
  var androidNotificationDetails = AndroidNotificationDetails(
    '0',
    '0',
    '0',
    importance: Importance.high,
    priority: Priority.high,
  );
  var iosNotificationDetails = IOSNotificationDetails();
  var notificationDetails = NotificationDetails(android: androidNotificationDetails, iOS: iosNotificationDetails);
  var localNotification = FlutterLocalNotificationsPlugin();
  await localNotification.show(0, 'title local', 'description local', notificationDetails);
}

void triggerReminderAttendanceDevelopment() async {
  final now = DateTime.now();
  final isolateId = Isolate.current.hashCode;
  print("start [$now] trigger reminder attendance! isolate=${isolateId} function='$triggerReminderAttendanceDevelopment'");
  var notificationHandler = NotificationHandler();
  await notificationHandler.initReminderAttendance();
  notificationHandler.doTriggerReminderAttendance();
  print("end [$now] trigger reminder attendance! isolate=${isolateId} function='$triggerReminderAttendanceDevelopment'");
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp();
  FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
  await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
    alert: true,
    badge: true,
    sound: true,
  );
  FirebaseMessaging.onMessage.listen((RemoteMessage message) {
    debugPrint('onMessage');
    debugPrint('Message data: ${message.data}');
    if (message.notification != null) {
      debugPrint('Message also contained a notification: ${message.notification}');
      debugPrint('Message also contained a title: ${message.notification.title}');
      debugPrint('Message also contained a body: ${message.notification.body}');
    }
  });
  FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
    debugPrint('onMessageOpenedApp');
    debugPrint('Message data: ${message.data}');
    if (message.notification != null) {
      debugPrint('Message also contained a notification: ${message.notification}');
      debugPrint('Message also contained a title: ${message.notification.title}');
      debugPrint('Message also contained a body: ${message.notification.body}');
    }
  });
  await FirebaseMessaging.instance.requestPermission();

  final baseUrlConfig = BaseUrlConfig();
  FlavorConfig(
    flavor: Flavor.DEVELOPMENT,
    /*colorPrimary: Color(0xFFF8F9FD),
    colorPrimaryDark: Color(0xFFF8F9FD),
    colorPrimaryLight: Color(0xFFF8F9FD),*/
    colorPrimary: Colors.green,
    colorAccent: Colors.green,
    values: FlavorValues(
      baseUrlFakeJsonEndpoint: baseUrlConfig.baseUrlDevelopmentFakeJsonEndpoint,
      baseUrlAuthEndpoint: baseUrlConfig.baseUrlDevelopmentAuthEndpoint,
      baseUrlEmployeeEndpoint: baseUrlConfig.baseUrlDevelopmentEmployeeEndpoint,
      baseUrlAttendanceEndpoint: baseUrlConfig.baseUrlDevelopmentAttendanceEndpoint,
      baseUrlRequestEndpoint: baseUrlConfig.baseUrlDevelopmentRequestEndpoint,
      baseUrlNotificationEndpoint: baseUrlConfig.baseUrlDevelopmentNotificationEndpoint,
      baseUrlSettingEndpoint: baseUrlConfig.baseUrlDevelopmentSettingEndpoint,
      baseUrlGeneralEndpoint: baseUrlConfig.baseUrlDevelopmentRootEndpoint,
      baseUrlPanelEndpoint: baseUrlConfig.baseUrlDevelopmentPanelEndpoint,
      baseUrlPayrollEndpoint: baseUrlConfig.baseUrlDevelopmentPayrollEndpoint,
    ),
  );
  await di.init();
  final sharedPreferencesManager = di.sl<SharedPreferencesManager>();
  if (sharedPreferencesManager.isKeyExists(SharedPreferencesManager.keyDomainApi)) {
    var domainApi = sharedPreferencesManager.getString(SharedPreferencesManager.keyDomainApi);
    FlavorConfig.instance.values.baseUrlAuthEndpoint = domainApi + '' + BaseUrlConfig().prefixAuthEndpoint;
    FlavorConfig.instance.values.baseUrlEmployeeEndpoint = domainApi + '' + BaseUrlConfig().prefixEmployeeEndpoint;
    FlavorConfig.instance.values.baseUrlAttendanceEndpoint = domainApi + '' + BaseUrlConfig().prefixAttendanceEndpoint;
    FlavorConfig.instance.values.baseUrlNotificationEndpoint = domainApi + '' + BaseUrlConfig().prefixNotificationEndpoint;
    FlavorConfig.instance.values.baseUrlRequestEndpoint = domainApi + '' + BaseUrlConfig().prefixRequestEndpoint;
    FlavorConfig.instance.values.baseUrlPayrollEndpoint = domainApi + '' + BaseUrlConfig().prefixPayrollEndpoint;
  }
  WidgetsBinding.instance.addPostFrameCallback((_) async {
    final methodChannelHelper = di.sl<MethodChannelHelper>();
    if (Platform.isAndroid) {
      await methodChannelHelper.initializeStetho();
    }
  });
  var triggerReminderAttendanceId = 0;
  if (Platform.isAndroid) {
    await AndroidAlarmManager.initialize();
  }
  runApp(
    EasyLocalization(
      supportedLocales: [
        Locale('en', 'US'),
        Locale('id', 'ID'),
      ],
      path: 'assets/translations',
      fallbackLocale: Locale('en', 'US'),
      child: App(),
    ),
  );
  if (Platform.isAndroid) {
    await AndroidAlarmManager.periodic(
      const Duration(hours: 1),
      triggerReminderAttendanceId,
      triggerReminderAttendanceDevelopment,
    );
  }
}
irufano commented 3 years ago

You can use the new dependency of firebase: firebase_messaging: 9.0.0 firebase_core: 1.0.1

Add settings for setup fcm:

NotificationSettings settings = await messaging.requestPermission(
      alert: true,
      announcement: false,
      badge: true,
      carPlay: false,
      criticalAlert: false,
      provisional: false,
      sound: true,
    );

You can add default sound at notification json :

"notification": {
        "title": "This is title...",
        "body": "This is body...",
        "sound": "default",
 },
"data" {
       // other stuff
}
russellwheatley commented 3 years ago

I'm going to close this issue as we're now on version 9.1.2. We've tried to carefully document behaviour for each application state (i.e foreground, background and terminated) for both android & iOS which you can read here. messaging is just one piece of the puzzle, even if you receive the data payload from firebase, it doesn't guarantee your notification will work as device/OS settings can affect their display.