How do I correctly register a plugin with the FCM plugin

2,216

Turns out it was just the plugin after my first edit! Just tried it with shared preferences and it works!

MY_PACKAGE_NAME

import `in`.jvapps.system_alert_window.SystemAlertWindowPlugin
import fr.g123k.deviceapps.DeviceAppsPlugin

import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin
import io.flutter.view.FlutterMain
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
import io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin

import android.os.Build
import android.app.NotificationManager
import android.app.NotificationChannel

public class Application: FlutterApplication(), PluginRegistrantCallback {

   override fun onCreate() {
     super.onCreate();
     FlutterFirebaseMessagingService.setPluginRegistrant(this);
     SystemAlertWindowPlugin.setPluginRegistrant(this);
     createNotificationChannels();
     FlutterMain.startInitialization(this);
   }

   override fun registerWith(registry: PluginRegistry?) {
    if (!registry!!.hasPlugin("io.flutter.plugins.firebasemessaging")) {
      FirebaseMessagingPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
    }
    if (!registry!!.hasPlugin("in.jvapps.system_alert_window")) {
      SystemAlertWindowPlugin.registerWith(registry!!.registrarFor("in.jvapps.system_alert_window"));
    }
    if (!registry!!.hasPlugin("plugins.flutter.io.shared_preferences")) {
      SharedPreferencesPlugin.registerWith(registry!!.registrarFor("plugins.flutter.io.shared_preferences"));
    }
   }

   fun createNotificationChannels() {
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val name = "groupChannel";
        val descriptionText = "This is the group channel";
        val importance = NotificationManager.IMPORTANCE_HIGH;
        val mChannel = NotificationChannel("59054", name, importance);
        mChannel.description = descriptionText;
        val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager;
        notificationManager.createNotificationChannel(mChannel);
    }
  }
}
Share:
2,216
anonymous-dev
Author by

anonymous-dev

Updated on December 22, 2022

Comments

  • anonymous-dev
    anonymous-dev over 1 year

    I am using https://pub.dev/packages/firebase_messaging v6.0.16 and flutter v1.17.5

    [√] Flutter (Channel stable, v1.17.5, on Microsoft Windows [Version 10.0.18362.900], locale en-US)
     
    [√] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
    [√] Android Studio (version 4.0)
    [√] VS Code (version 1.47.0)
    

    And I am trying to call a plugin inside my myBackgroundMessageHandler on the line where it says AppAvailability.launchApp('com.companyname.appname');

    class CloudMessagingService extends NotificationService {
      final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
    
      void configure() {
        if (Platform.isIOS) _firebaseMessaging.requestNotificationPermissions(IosNotificationSettings());
    
        _firebaseMessaging.configure(
          onMessage: (Map<String, dynamic> message) async {
            if (isDataNotification(message)) return;
            print('onMessage');
          },
          onLaunch: (Map<String, dynamic> message) async {
            if (isDataNotification(message)) return;
            print('onLaunch');
          },
          onResume: (Map<String, dynamic> message) async {
            if (isDataNotification(message)) return;
            print('onResume');
          },
          onBackgroundMessage: myBackgroundMessageHandler,
        );
      }
    
      static Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async {
        print('on background message');
        AppAvailability.launchApp('com.companyname.appname'); // it will throw an error when it tries to call the method channel in the app availability plugin
        return message;
      }
    

    But I get the following error

    E/flutter (28000): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: MissingPluginException(No implementation found for method launchApp on channel com.pichillilorenzo/flutter_appavailability)
    E/flutter (28000): #0      MethodChannel._invokeMethod 
    package:flutter/…/services/platform_channel.dart:154
    E/flutter (28000): <asynchronous suspension>
    E/flutter (28000): #1      MethodChannel.invokeMethod 
    package:flutter/…/services/platform_channel.dart:329
    E/flutter (28000): #2      AppAvailability.launchApp 
    package:flutter_appavailability/flutter_appavailability.dart:95
    E/flutter (28000): #3      CloudMessagingService.myBackgroundMessageHandler 
    package:moto_picto/…/firebase/cloud_messaging_service.dart:43
    E/flutter (28000): #4      _fcmSetupBackgroundChannel.<anonymous closure> 
    package:firebase_messaging/firebase_messaging.dart:38
    E/flutter (28000): #5      MethodChannel._handleAsMethodCall 
    package:flutter/…/services/platform_channel.dart:409
    E/flutter (28000): #6      MethodChannel.setMethodCallHandler.<anonymous closure> 
    package:flutter/…/services/platform_channel.dart:377
    E/flutter (28000): #7      _DefaultBinaryMessenger.handlePlatformMessage 
    package:flutter/…/services/binding.dart:199
    E/flutter (28000): #8      _invoke3.<anonymous closure>  (dart:ui/hooks.dart:290:15)
    E/flutter (28000): #9      _rootRun  (dart:async/zone.dart:1184:13)
    E/flutter (28000): #10     _CustomZone.run  (dart:async/zone.dart:1077:19)
    E/flutter (28000): #11     _CustomZone.runGuarded  (dart:async/zone.dart:979:7)
    E/flutter (28000): #12     _invoke3  (dart:ui/hooks.dart:289:10)
    E/flutter (28000): #13     _dispatchPlatformMessage  (dart:ui/hooks.dart:164:5)
    

    The plugins work without any trouble in the app scope. This is happening for every plugin I tried!

    So I think the problem is that the isolate that FCM makes isn't wrapped with the flutter engine from what I understand from this post.

    https://github.com/flutter/flutter/issues/13937#issuecomment-656239596

    but you need to create a custom Application class that hooks into the initialisation of the plugin's background FlutterNativeView.

    I did follow the instructions and changed my Application.kt to the following

    package com.companyname.appname
        
    import `in`.jvapps.system_alert_window.SystemAlertWindowPlugin
    import io.flutter.app.FlutterApplication
    import io.flutter.plugin.common.PluginRegistry
    import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
    import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
    import io.flutter.view.FlutterMain
    import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin
    
    import android.os.Build
    import android.app.NotificationManager
    import android.app.NotificationChannel
    
    public class Application: FlutterApplication(), PluginRegistrantCallback {
    
       override fun onCreate() {
         super.onCreate();
         FlutterFirebaseMessagingService.setPluginRegistrant(this);
         createNotificationChannels();
         SystemAlertWindowPlugin.setPluginRegistrant(this);
         FlutterMain.startInitialization(this);
       }
    
       override fun registerWith(registry: PluginRegistry) {
        if (!registry!!.hasPlugin("io.flutter.plugins.firebasemessaging")) {
          FirebaseMessagingPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
        }
        if (!registry!!.hasPlugin("in.jvapps.system_alert_window")) {
          SystemAlertWindowPlugin.registerWith(registry!!.registrarFor("in.jvapps.system_alert_window"));
        }
       }
    
       fun createNotificationChannels() {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val name = "groupChannel";
            val descriptionText = "This is the group channel";
            val importance = NotificationManager.IMPORTANCE_HIGH;
            val mChannel = NotificationChannel("59054", name, importance);
            mChannel.description = descriptionText;
            val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager;
            notificationManager.createNotificationChannel(mChannel);
        }
      }
    }
    

    and my MainActivity.kt

    package com.companyname.appname
    
    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);
        }
    }
    

    And my AndroidManifest.xml

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.companyname.appname">
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        <uses-permission android:name="android.permission.READ_CONTACTS" />
        <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
        <uses-permission android:name="android.permission.FOREGROUND_SERVICE " />
        <uses-permission android:name="android.permission.WAKE_LOCK" />
        <!-- 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. -->
        <application
            android:name=".Application"
            android:label="App Name"
            android:icon="@mipmap/ic_launcher">
            <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">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
                <intent-filter>
                    <action android:name="FLUTTER_NOTIFICATION_CLICK" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>
            <activity
            android:name="com.yalantis.ucrop.UCropActivity"
            android:screenOrientation="portrait"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
            <!-- 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" />
            <meta-data
                android:name="com.google.firebase.messaging.default_notification_channel_id"
                android:value="@string/default_notification_channel_id"/>  
        </application>
    </manifest>
    

    The notifications seem to be appearing correctly as heads up notifications, and I can call my BackgroundMessageHandler and execute code, it's just that I cannot do anything with any plugin in the myBackgroundMessageHandler. Did I implement it correctly?

    It will always throw the error on this line AppAvailability.launchApp('com.companyname.appname'); in the myBackgroundMessageHandler because a method channel cannot be called by a isolate if the plugin is not registered.

    I also checked out this github post

    Similar problem

    But my Application.kt is setup like that

    I tried doing flutter clean -> uninstalling the app -> installing the app as well. And the problem remains. Like they describe here

    MissingPluginException(No implementation found for method show on channel dexterous.com/flutter/local_notifications)

    EDIT: When I use the DeviceApp plugin the same thing happens. I replaced

    AppAvailability.launchApp('com.companyname.appname');
    

    with

     DeviceApps.openApp('com.companyname.appname');
    

    So I added the following 2 lines to the registerWith method of Application.kt, and it seems to revolve the missing plugin exception

    if (!registry!!.hasPlugin("fr.g123k.deviceapps")) {
      DeviceAppsPlugin.registerWith(registry!!.registrarFor("fr.g123k.deviceapps"));
    }
    

    But the error changes to

    E/flutter (25583): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: PlatformException(error, Attempt to invoke virtual method 'android.content.pm.PackageManager android.app.Activity.getPackageManager()' on a null object reference, null)
    E/flutter (25583): #0      StandardMethodCodec.decodeEnvelope 
    package:flutter/…/services/message_codecs.dart:569
    E/flutter (25583): #1      MethodChannel._invokeMethod 
    package:flutter/…/services/platform_channel.dart:156
    E/flutter (25583): <asynchronous suspension>
    E/flutter (25583): #2      MethodChannel.invokeMethod 
    package:flutter/…/services/platform_channel.dart:329
    E/flutter (25583): #3      DeviceApps.openApp 
    package:device_apps/device_apps.dart:81
    E/flutter (25583): #4      CloudMessagingService.myBackgroundMessageHandler 
    package:moto_picto/…/firebase/cloud_messaging_service.dart:73
    E/flutter (25583): #5      _fcmSetupBackgroundChannel.<anonymous closure> 
    package:firebase_messaging/firebase_messaging.dart:38
    E/flutter (25583): #6      MethodChannel._handleAsMethodCall 
    package:flutter/…/services/platform_channel.dart:409
    E/flutter (25583): #7      MethodChannel.setMethodCallHandler.<anonymous closure> 
    package:flutter/…/services/platform_channel.dart:377
    E/flutter (25583): #8      _DefaultBinaryMessenger.handlePlatformMessage 
    package:flutter/…/services/binding.dart:199
    E/flutter (25583): #9      _invoke3.<anonymous closure>  (dart:ui/hooks.dart:290:15)
    E/flutter (25583): #10     _rootRun  (dart:async/zone.dart:1184:13)
    E/flutter (25583): #11     _CustomZone.run  (dart:async/zone.dart:1077:19)
    E/flutter (25583): #12     _CustomZone.runGuarded  (dart:async/zone.dart:979:7)
    E/flutter (25583): #13     _invoke3  (dart:ui/hooks.dart:289:10)
    E/flutter (25583): #14     _dispatchPlatformMessage  (dart:ui/hooks.dart:164:5)
    

    So now it is no longer saying plugin missing but it is saying PlatformException(error, Attempt to invoke virtual method 'android.content.pm.PackageManager android.app.Activity.getPackageManager()

    Edit 2:

    I just noticed that the latest error https://github.com/g123k/flutter_plugin_device_apps/issues/31 might be one of the plugin itself. Going to see if different plugins work now

    I hope I provided enough info, please let me know if more info is needed or if the question needs to altered. I can respond within a couple off minutes.