'How to get a full screen notification from FCM?

I am building a home automation project that has a fire sensor that will write to Firebase Database if there is a fire detected, then from that point I need to make an alarm for the user. I managed to trigger a notification from Firebase cloud functions, but that's not exactly what I want. What I want is to make a full-screen notification to the user with a custom sound something like a phone alarm or a what's app call when there is a fire alarm -change in the database-.

I tried as a Top level function with no error while running my application:

  firebaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async {
        print('onMessage: $message');
        toast3('asdasdsawwwww $message');
        setMessage(message);
      },
      onLaunch: (Map<String, dynamic> message) async {
        print('onLaunch: $message');
        setMessage(message);
      },
      onResume: (Map<String, dynamic> message) async {
        print('onResume: $message');
        setMessage(message);
      },
      onBackgroundMessage: myBackgroundMessageHandler);
  print('onMessage:12qew11');

  firebaseMessaging.requestNotificationPermissions(
    const IosNotificationSettings(sound: true, badge: true, alert: true),
  );
}


Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) {
  print('HEREE');
 final assetsAudioPlayer = AssetsAudioPlayer();

    assetsAudioPlayer.open(
        Audio("assets/audio/alarm.mp3"),
    );
  return Fluttertoast.showToast(
      msg: 'done background:))))$message',
      toastLength: Toast.LENGTH_LONG,
      gravity: ToastGravity.BOTTOM,
      timeInSecForIos: 4,
      backgroundColor: Colors.redAccent,
      textColor: Colors.white,
      fontSize: 15.0);
}

My Firebase function:

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp(functions.config().functions);

var fireDatabase;
exports.myFirstCloudFun = functions.database.ref('/usersData/{userID}/Fire').onUpdate(async (event, context) => {

    const uidGotten = context.params.userID;

    const fireData = event.after.val()
    console.log('data changed in fire is' + fireData + 'userID is ' + uidGotten);
    const usereIdTokens = await admin
        .firestore()
        .collection(uidGotten)
        .doc('userTokens')
        .get();
    console.log('Tokens to try are' + usereIdTokens.data);

    var tokens = usereIdTokens.data().user_all_tokens;
    var payload = {
        notification: {
            title: 'Push Title',
            body: 'Push Body' + fireData,
            sound: 'default',
        },
        data: {
            push_key: 'Fire Value Is',
            key1: "fireData is " + fireData,
        },
    };
    tokens.forEach.toString().trim;

    console.log('Tokens to send are ' + tokens[1] + '   //////   ' + tokens);

    try {
        const response = await admin.messaging().sendToDevice(tokens, payload);
        console.log('Notification sent successfully');
    } catch (err) {
        console.log(err);
    }
});

My Application.kt

package com.eghubs.eg_home_hubs

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: FlutterApplication(), PluginRegistrantCallback {
    override fun onCreate() {
        super.onCreate()
        FlutterFirebaseMessagingService.setPluginRegistrant(this)
    }

    override fun registerWith(registry: PluginRegistry) {
        FirebaseCloudMessagingPluginRegistrant.registerWith(registry)
    }
}

My FirebaseCloudMessagingPluginRegistrant.kt

package com.eghubs.eg_home_hubs


import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin

class FirebaseCloudMessagingPluginRegistrant {
    companion object {
        fun registerWith(registry: PluginRegistry) {
            if (alreadyRegisteredWith(registry)) {
                return;
            }
            FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"))
        }

        fun alreadyRegisteredWith(registry: PluginRegistry): Boolean {
            val key = FirebaseCloudMessagingPluginRegistrant::class.java.name
            if (registry.hasPlugin(key)) {
                return true
            }
            registry.registrarFor(key)
            return false
        }
    }
}

My AndroidManifest is :

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.eghubs.eg_home_hubs">
    <uses-permission android:name="android.permission.ACCESS_CORSE_LOCATION" />
    <uses-permission android:name="android.permission.INTERNET"/>



    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.
         In most cases you can leave this as-is, but you if you want to provide
         additional functionality it is fine to subclass or reimplement
         FlutterApplication and put your custom class here.
                  android:name="io.flutter.app.FlutterApplication"
                  android:name="androidx.multidex.MultiDexApplication"

-->
    <application
        android:name=".Application" <!-- here is the change-->
        android:label="EG HomeHubs"
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher">
        tools:replace="android:allowBackup">
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            android:showWhenLocked="true"
            android:turnScreenOn="true">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <!-- Displays an Android View that continues showing the launch screen
                 Drawable until Flutter paints its first frame, then this splash
                 screen fades out. A splash screen is useful to avoid any visual
                 gap between the end of Android's launch screen and the painting of
                 Flutter's first frame. -->
            <meta-data
              android:name="io.flutter.embedding.android.SplashScreenDrawable"
              android:resource="@drawable/launch_background"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
            <intent-filter>   <!-- Noti:this is for cloud messiging -->
                <action android:name="FLUTTER_NOTIFICATION_CLICK" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>

</manifest>

but I get nothing when the application is not opened in the background, why onBackgroundMessage function is not called?

My question is how can I achieve that what's an app call or something of that kind?

OR Is there any better way to do this, some other way to achieve that fire alarm functionality in my project from the firebase database change?

EDIT: My 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 FileNotFoundException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
//GradleException

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"
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
    packagingOptions {
        exclude 'META-INF/services/javax.annotation.processing.Processor'
    }
    compileSdkVersion 30

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

    lintOptions {
        disable 'InvalidPackage'
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.eghubs.eg_home_hubs"
        minSdkVersion 21
        targetSdkVersion 30
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
      //  multiDexEnabled true

    }
    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
        debug {
            minifyEnabled true
            shrinkResources true
      }

    }
}

flutter {
    source '../..'
}

dependencies {
    implementation 'com.google.firebase:firebase-analytics'
    implementation platform('com.google.firebase:firebase-bom:26.0.0')
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
  //  implementation 'androidx.multidex:multidex:2.0.1'  //with androidx libraries
    implementation'com.google.firebase:firebase-messaging:21.0.1'


}
apply plugin: 'com.android.application'
// Add this line
apply plugin: 'com.google.gms.google-services'

My android/build.gradle:

buildscript {
    ext.kotlin_version = '1.3.50'
    repositories {
        google()
        jcenter()
    }

    dependencies {
        classpath 'com.google.gms:google-services:4.3.4'
        //classpath 'com.android.tools.build:gradle:3.5.3' //todo rollback this
        classpath 'com.android.tools.build:gradle:4.1.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

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
}


Solution 1:[1]

Alright.

Since update 8.0.0-dev.1 we can get onBackgroundMessage by default without any need for these installation process

NEW: FirebaseMessaging.onBackgroundMessage() Sets a background message handler to trigger when the app is in the background or terminated.

Solution 2:[2]

I have some solutions for solving this problem.

Solution 1:

The first thing you have to do is to check if onBackgroundMessage is supported in iOS or not.
And in Android, you have to turn on the Allow running in background option.

Solution 2:

Create a high importance channel through the flutter_local_notifications package.
flutter_local_notifications package link: https://pub.dev/packages/flutter_local_notifications

Solution 3:

  1. Create a new file App.java inside 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, inside AndroidManifest.xml file, add android:name=".App"
<application
        android:name=".App"
       ...
 >

After that rebuild the application and the notifications will work correctly even when the app is terminated or killed.

Solution 3:[3]

On Android, for your onBackgroundMessage to be called when the app is in the background the FCM message must be a data message without notification, look here: https://firebase.google.com/docs/cloud-messaging/android/receive

Also when the device is sleeping, to receive the messages without much delay you should disable the battery optimization, look at this: https://developer.android.com/training/monitoring-device-state/doze-standby

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Shalabyer
Solution 2 Ankush Chavan
Solution 3