Flutterfire: [firebase_messaging] 'registrar.activity() must not be null' exception occurs in 'onBackgroundMessage'

Created on 21 Jan 2020  ·  9Comments  ·  Source: FirebaseExtended/flutterfire

Bug Description

When the app goes to the fcm configure : _firebaseMessaging.configure(),
it throws exception

PlatformException (PlatformException(error, registrar.activity() must not be null, null))

I also tried handler method as STATIC, but it throws the same exception.

To Reproduce

Steps to reproduce the behavior:

  1. Define TOP-LEVEL or STATIC background handler method myBackgroundMessageHandler
  2. Config myBackgroundMessageHandler as onBackgroundMessage inside the _firebaseMessaging.configure()
  3. Run
  4. It throws exception when the process dive into the _firebaseMessaging.configure()
  5. When I remove onBackgroundMessage as comment, it runs normally.

My codes

homePage.dart

final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();

String _urlBase = url.urlBase;

Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) {
  if (message.containsKey('data')) {
    // Handle data message
    final dynamic data = message['data'];
    print(data);
  }
  if (message.containsKey('notification')) {
    // Handle notification message
    final dynamic notification = message['notification'];
    print(notification);
  }
  // Or do other work.
}

class Homepage extends StatefulWidget {
  Homepage({Key key, this.pageIndex}) : super(key: key);
  final pageIndex;
  @override
  _HomepageState createState() => _HomepageState();
}

class _HomepageState extends State<Homepage> {
  //URL launcher
  static const platform = const MethodChannel('example.com/channel');

  ScrollController _controller;
  @override
  void initState() {
    super.initState();

    _regChannel();
    _firebaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async {
        try {
          await getUserNotificationLists();

          String iconImg;

          for (var eachNotification in _userNotificationList) {
            if (eachNotification['post_id'] == message['data']['post_id']) {
              iconImg = eachNotification['icon_img'];
              break;
            }
          }

          showOverlayNotification((context) {
            return overlayNotificationCard(
                message['notification']['title'],
                message['notification']['body'],
                iconImg,
                message['data']['post_id'],
                context);
          }, duration: Duration(milliseconds: 5000));
          print("onMessage: $message");
        } catch (e) {
          print(e);
        }
      },
      onBackgroundMessage: Platform.isIOS ? null : myBackgroundMessageHandler,
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
      },
      onResume: (Map<String, dynamic> message) async {
        print("onResume: $message");
      },
    );
    _firebaseMessaging.requestNotificationPermissions(
        const IosNotificationSettings(sound: true, badge: true, alert: true));
    _firebaseMessaging.onIosSettingsRegistered
        .listen((IosNotificationSettings settings) {
      print("Settings registered: $settings");
    });

    getUserNotificationLists();

    _getEvent();
    _getPinnedEvent();
    _checkUser();
    _checkFcmToken();
    _widgetIndex = widget.pageIndex;
    _bIndex = widget.pageIndex;
    _controller = ScrollController();
  }
.
.
.

Application.kt

package com.example.event

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

class Application : FlutterApplication(), PluginRegistry.PluginRegistrantCallback {
    override fun onCreate() {
        super.onCreate()
        FlutterFirebaseMessagingService.setPluginRegistrant(this)
    }

    override fun registerWith(registry: PluginRegistry?) {
        GeneratedPluginRegistrant.registerWith(registry)
    }

}

MainActivity.kt

package com.example.event

import android.os.Bundle
import io.flutter.app.FlutterActivity
import io.flutter.plugins.GeneratedPluginRegistrant

import io.flutter.plugin.common.MethodChannel

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

class MainActivity: FlutterActivity() {

  private val CHANNEL = "example.com/channel"

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    GeneratedPluginRegistrant.registerWith(this)
    MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->
      if (call.method == "getChannel") {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
          val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
          val cid = getString(R.string.default_notification_channel_id)
          val checkChannel = notificationManager.getNotificationChannel(cid)

          if(checkChannel == null) {
            // Create the NotificationChannel
            val name = "default"
            val importance = NotificationManager.IMPORTANCE_MAX
            val mChannel = NotificationChannel(cid, name, importance)
            mChannel.description = "default desc"

            // Register the channel with the system; you can't change the importance
            // or other notification behaviors after this
            notificationManager.createNotificationChannel(mChannel)

            result.success("registerd!")
          }
          else{
            result.success("already registerd!")
          }
        }
      } else {
        result.notImplemented()
      }
    }
  }
}

android/build.gradle

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

    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.3'
        classpath 'com.google.gms:google-services:4.3.2'
        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
}

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.1'
}

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

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
    compileSdkVersion 28

    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.example.event"
        minSdkVersion 21
        targetSdkVersion 28
        multiDexEnabled true
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    signingConfigs {
       release {
           keyAlias keystoreProperties['keyAlias']
           keyPassword keystoreProperties['keyPassword']
           storeFile file(keystoreProperties['storeFile'])
           storePassword keystoreProperties['storePassword']
       }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release

            minifyEnabled true
            useProguard true

            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
       }
    }
}

flutter {
    source '../..'
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.facebook.android:facebook-login:[5,6)'
    implementation "com.google.firebase:firebase-messaging:20.1.0"
    // implementation "com.google.firebase:firebase-analytics:17.2.1"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'

}

apply plugin: 'com.google.gms.google-services'  // Google Play services Gradle plugin

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.event">

    <!-- 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="io.flutter.app.FlutterApplication" -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <application
        android:name=".Application"
        android:label="EXAMPLE"
        android:icon="@mipmap/ic_launcher">
        <meta-data android:name="com.google.android.geo.API_KEY"
                android:value="AbcDeFgHiJKLMNopqrstuvwxyzdklE99dkkdfsdfs"/>
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- This keeps the window background of the activity showing
                 until Flutter renders its first frame. It can be removed if
                 there is no splash screen (such as the default splash screen
                 defined in @style/LaunchTheme). -->
            <meta-data
                android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                android:value="true" />
            <meta-data
                android:name="com.google.firebase.messaging.default_notification_channel_id"
                android:value="@string/default_notification_channel_id"/>
            <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>

        <!-- facebook login -->
        <meta-data android:name="com.facebook.sdk.ApplicationId"
            android:value="@string/facebook_app_id"/>

        <activity android:name="com.facebook.FacebookActivity"
            android:configChanges=
                    "keyboard|keyboardHidden|screenLayout|screenSize|orientation"
            android:label="@string/app_name" />

        <activity
            android:name="com.facebook.CustomTabActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="@string/fb_login_protocol_scheme" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Exception Details

homePage.dart

스크린샷 2020-01-22 오전 4 40 56

firebase_messaging.dart

스크린샷 2020-01-22 오전 4 41 16

platform_channel.dart

스크린샷 2020-01-22 오전 4 41 27

message_codecs.dart

스크린샷 2020-01-22 오전 4 41 45

Development Env

flutter doctor

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.12.13+hotfix.5, on Mac OS X 10.14.6 18G2022, locale ko-KR)

[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 11.1)
[✓] Android Studio (version 3.5)
[✓] VS Code (version 1.41.1)
[✓] Connected device (1 available)

• No issues found!
crowd messaging bug

Most helpful comment

I have the same issue here, no solutions yet? :(

Edit:
I found this solution: https://stackoverflow.com/a/59490722

Instead of:
GeneratedPluginRegistrant.registerWith(registry);

Use:
FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));

My change was not just that. I used the entire class created by DomingoMG and it worked like a charm.

All 9 comments

same here.

getting the same error as well .

Same issue.

same issue.

same issue

i see the Application file:

override fun registerWith(registry: PluginRegistry?) {
        GeneratedPluginRegistrant.registerWith(registry)
    }

register a PluginRegistry type, but in file GeneratedPluginRegistrant, need "FlutterEngine" type

public static void registerWith(@NonNull FlutterEngine flutterEngine) {
(is auto generated)

i see an error in this documentation plugin
I don't know how to solve it

I'm having the same issue, any solutions?

I have the same issue here, no solutions yet? :(

Edit:
I found this solution: https://stackoverflow.com/a/59490722

Instead of:
GeneratedPluginRegistrant.registerWith(registry);

Use:
FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));

My change was not just that. I used the entire class created by DomingoMG and it worked like a charm.

Was this page helpful?
0 / 5 - 0 ratings