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
}
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:
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();
}
}
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.
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
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.
Related
I'm trying to using phone verification with firebase. My code works perfectly on debug as well as in release mode, but when I upload my app to the Play Store, it's not working.
Here is the error it gives:
'This request is missing a valid app identifier, meaning that neither SafetyNet checks nor reCAPTCHA checks succeeded. Please try again, or check the logcat for more details.'
I have added all the keys to firebase, enabled Android Device Verification API, and still it's giving same error.
Here is the code I have used for phone verification:
Future<void> onVerifyCode(BuildContext context, UserModel userModel) async {
print('i am verifycode');
void verificationDone(PhoneAuthCredential credential) async {
await _firebaseAuth.signInWithCredential(credential).then((value) {
if (value.user != null) {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => AccountDetails(
userModel: userModel,
),
),
);
} else {
Fluttertoast.showToast(msg: "Error validating OTP, try again");
}
}).catchError((error) {
print('verify done error' + error);
Fluttertoast.showToast(msg: '$error');
});
}
void verificationFail(FirebaseAuthException e) {
print('i am fail' + e.message);
Fluttertoast.showToast(msg: '${e.message}');
}
void codeSent(String verificationId, int resendToken) async {
print('i am codeSent');
_verificationId = verificationId;
// dialogBox(verificationId, resendToken);
// Create a PhoneAuthCredential with the code
}
final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
(String verificationId) {
_verificationId = verificationId;
};
await _firebaseAuth
.verifyPhoneNumber(
phoneNumber: "+91${userModel.phone}",
timeout: const Duration(seconds: 60),
verificationCompleted: verificationDone,
verificationFailed: verificationFail,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout)
.catchError((error) {
print('verifyPhoneNo' + error);
Fluttertoast.showToast(msg: '$error');
});
}
Here is my build.gradle(app level):
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: 'com.google.gms.google-services'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
compileSdkVersion 30
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.shdigital.vasts"
minSdkVersion 21
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.release
minifyEnabled true
useProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "androidx.browser:browser:1.3.0"
implementation 'com.android.support:multidex:1.0.3'
}
Here is my build.gradle(project level):
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.google.gms:google-services:4.3.4'
}
}
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
}
Here is my pub.yaml file:
name: demoappname
description: A new Flutter application.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.3+4
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.0
provider: ^4.3.2+4
video_player: ^2.0.2
chewie: ^1.0.0
flutter_windowmanager: ^0.0.2
dio: ^3.0.10
path_provider: ^1.6.27
path: ^1.7.0
#firebase
firebase_core: "^0.7.0"
firebase_auth: "^0.20.1"
connectivity: ^2.0.2
pin_code_text_field: ^1.7.1
share: ^0.6.5+4
http: ^0.12.2
fluttertoast: ^7.1.6
shared_preferences: ^0.5.12+4
razorpay_flutter: 1.1.0
image_picker: ^0.6.7+21
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
assets:
- assets/images/
I am very new to flutter and I am making a login and register authentication using firebase.
I completed all the steps from setting up the app in firebase to adding all the required dependencies and all that. Below are the files.
project level build.gradle
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.google.gms:google-services:4.3.3'
}
}
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
}
app level 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.")
}
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: 'com.google.gms.google-services'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 29
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "co.amanchaudhary.flash_chat"
minSdkVersion 16
targetSdkVersion 29
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
}
buildTypes {
release {
// 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
}
}
}
flutter {
source '../..'
}
dependencies {
implementation 'androidx.multidex:multidex:2.0.0'
implementation 'com.google.firebase:firebase-analytics:17.5.0'
}
And here is the error in the console,
In the beginning the I/BiChannelGoogleApi was shown, whereas now, the two others are also shown in the console.
I/BiChannelGoogleApi( 4964): [FirebaseAuth: ] getGoogleApiForMethod() returned Gms: com.google.firebase.auth.api.internal.zzaq#b13d73
W/DynamiteModule( 4964): Local module descriptor class for com.google.firebase.auth not found.
I/FirebaseAuth( 4964): [FirebaseAuth:] Preparing to create service connection to gms implementation
I am calling, when registered button is clicked,
ButtonStyle(
buttonColor: Colors.blueAccent,
buttonText: "Register",
onPressedButton: () async{
try {
final newUser = await _auth.createUserWithEmailAndPassword(email: email, password: password);
if(newUser !=null){
Navigator.pushNamed(context, ChatScreen.id);
}
} catch (e) {
print(e);
}
},
),
This exception occurs when clicking the register button,
i want to sign-in with Google account my app but i get
ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: PlatformException(sign_in_failed, com.google.android.gms.common.api.ApiException: 10: , null)
main.dart:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_auth_buttons/flutter_auth_buttons.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _value=false;
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
Future<String> signInWithGoogle() async {
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final AuthResult authResult = await _auth.signInWithCredential(credential);
final FirebaseUser user = authResult.user;
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
return 'signInWithGoogle succeeded: $user';
}
void signOutGoogle() async{
await googleSignIn.signOut();
print("User Sign Out");
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
body:
Center(
child:
GoogleSignInButton(
onPressed: () {
signInWithGoogle().whenComplete(() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return FirstScreen();
},
),
);
});
},
darkMode: true, // default: false
)
)
// This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class FirstScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(color: Colors.blue[100]),
);
}
}
project/build.gradle:
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
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'
}
}
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
}
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 plugin: 'com.google.gms.google-services'
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 {
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 "batuhan.todo"
minSdkVersion 16
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"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
implementation 'com.google.firebase:firebase-analytics:17.2.0'
}
I have key.jks and i added to Firebase sha-1 and sha-256 then update google-services.json. I ran Flutter clean. I enabled permission with Google in Firebase.
How can i fix this issue.
I guess i tried every solution...
"ApiException 10" thrown from Google Sign In is caused when the SHA-1 of the signing certificate of the app isn't configured correctly on the OAuth2 client and API key for the app. https://www.google.com/search?q=firebase+auth+apiexception+10
If you're running the app on debug, as previously mentioned in the comments, you'll need to add the SHA-1 generated from the debug keystore in the app settings of the Firebase Project. Same goes if the app is run on release, the SHA-1 from the release keystore should be added.
I'm trying to use FCM messaging and keep getting this error.
E/FlutterFcmService( 3684): Fatal: failed to find callback
Below is the code I've used to setup.
static Future<void> messagePiper(
Map<String, dynamic> message,
FilteredMap<String, ChatMessage> globalChatEntryMap,
FilteredMap<String, ChatMessage> gameChatEntryMap,
Subject<List<ChatMessage>> globalChatSubject,
Subject<List<ChatMessage>> gameChatSubject,
Map<String, Player> _playerMap) async {
final Map<String, dynamic> data = message['data'];
if (data.containsKey('name')) {
final msg = ChatMessage.fromMap(data);
globalChatEntryMap.putIfAbsent(msg.id, () => msg);
globalChatSubject.add(globalChatEntryMap.values.toList());
} else {
final msg = GameChatMessage.fromMap(data);
final chat = ChatMessage.fromGlobalChatMessage(
msg,
_playerMap[msg.pId].name,
_playerMap[msg.pId].imageUrl,
);
print('chat: $chat');
gameChatEntryMap.putIfAbsent(msg.id, () => chat);
print('_gameChatEntryMap : $gameChatEntryMap');
gameChatSubject.add(gameChatEntryMap.values.toList());
}
return Future<void>.value();
}
is the callback passed in to FirebaseMessaging configuration.
final FirebaseMessaging _fm = FirebaseMessaging();
#override
void initState() {
_fm.configure(
onMessage: (Map<String, dynamic> message) async {
print('onMessagee : $message');
return Utils.messagePiper(
message,
_globalChatEntryMap,
_gameChatEntryMap,
_globalChatSubject,
_gameChatSubject,
_playerMap);
},
onLaunch: (Map<String, dynamic> message) async {
print('onLaunch : $message');
return Utils.messagePiper(
message,
_globalChatEntryMap,
_gameChatEntryMap,
_globalChatSubject,
_gameChatSubject,
_playerMap);
;
},
onResume: (Map<String, dynamic> message) async {
print('onResume : $message');
return Utils.messagePiper(
message,
_globalChatEntryMap,
_gameChatEntryMap,
_globalChatSubject,
_gameChatSubject,
_playerMap);
;
},
onBackgroundMessage: null);
....
Java configuration file
package io.flutter.plugins;
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;
public class Application extends FlutterApplication implements PluginRegistrantCallback {
#Override
public void onCreate() {
super.onCreate();
FlutterFirebaseMessagingService.setPluginRegistrant(this);
}
#Override
public void registerWith(PluginRegistry registry) {
GeneratedPluginRegistrant.registerWith(registry);
}
}
dependency versions
random_string: 0.0.2
firebase_auth: ^0.14.0+5
firebase_database: ^3.0.7
provider: 3.0.0
rxdart: ^0.22.2
collection: ^1.14.11
audioplayers: ^0.13.2
firebase_admob: ^0.5.5
connectivity: ^0.4.4
firebase_messaging: ^5.1.6 # tried with several different versions
I tried with several firebase_messaging versions but couldn't find a fix.
Appreciate any help to solve this issue.
This error message is coming from startBackgroundIsolate which is used for allowing handling background messages.
If you don't want to handle background messages then you can safely ignore this error message. Otherwise, you need to set up a callback for handling background messages as described here
If your callback is not executed when clicking on the notification then it's because you didn't set click_action property of the message to FLUTTER_NOTIFICATION_CLICK
Are you sending FCM using the web, not FCM console?
make sure the post request is correct on your backend. I'm using Laravel
$response = Http::withHeaders([
'Content-Type' => 'application/json',
'Authorization'=> 'key='. $token,
])->post($url, [
'notification' => [
'body' => $request->summary,
'title' => $request->title,
'image' => request()->getHttpHost().$path,
],
'priority'=> 'high',
'data' => [
'click_action'=> 'FLUTTER_NOTIFICATION_CLICK',
'status'=> 'done',
],
'to' => '/topics/all'
]);
You should declare a backgroundMessageHandler function that is outside a class or as a static function, in order to be reached from outside, and then you pass this function to fbm.configure:
Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) {
print('on background $message');
}
fbm.configure(
onMessage: (msg) {
print(msg);
return;
},
onLaunch: (msg) {
print(msg);
return;
},
onResume: (msg) {
print(msg);
return;
},
onBackgroundMessage: myBackgroundMessageHandler
);
Also open your_project_folder/android/app/source/AndroidManifest.xml and paste this XML code after existing intent-filter code of your main Activity:
<intent-filter>
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
The result will be similar to the following code:
<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>
In Application class use the below code
previously it was GeneratedPluginRegistrant.registerWith(registry);,
replace it with
FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
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.FirebaseMessagingPlugin;
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){
FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
}
}
When I was trying to send a notification from the firebase console just to test my app receives notifications, I also encountered the same error. Make sure you kill the app on your emulator so it is not running in the background. And now try sending a notification from the firebase console, wait for a couple of seconds and you should see it. This worked for me.
I started with React Native recently, and it comes up that I had to use Push Notifications to notify all the users when a certain action is triggered,
so alright, I read the Firebase docs, configured the project, installed the dependencies, and I think that I've done everything alright, but my application closes when I try to send any notification to the app, and it's driving me crazy, because I haven't seen any other post with this problem yet, and I simply don't have the less idea on how to correct it, my deadline is coming, I'm gonna let all the configuration files here for you guys, and the actual code that handles the notification, even though it still does just a console.warn() on the message.
The app/build.gradle file:
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.watcher"
minSdkVersion 23
targetSdkVersion 27
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86"
}
}
buildTypes {
release {
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
def versionCodes = ["armeabi-v7a":1, "x86":2]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
}
dependencies {
implementation project(':react-native-vector-icons')
implementation project(':react-native-firebase')
implementation project(':react-native-fcm')
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:27.0.+"
implementation "com.facebook.react:react-native:+" // From node_modules
implementation 'com.google.firebase:firebase-core:16.0.1'
implementation("com.google.firebase:firebase-messaging:17.0.0") {
force = true
}
}
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}
apply plugin: 'com.google.gms.google-services'
The build.gradle file:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.0'
classpath 'com.google.gms:google-services:3.2.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
google()
}
}
MainApplication.java:
package com.watcher;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.oblador.vectoricons.VectorIconsPackage;
import io.invertase.firebase.RNFirebasePackage;
import com.evollu.react.fcm.FIRMessagingPackage;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage; // <-- Add this line
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
#Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
#Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new VectorIconsPackage(),
new RNFirebasePackage(),
new FIRMessagingPackage(),
new RNFirebaseMessagingPackage()
);
}
#Override
protected String getJSMainModuleName() {
return "index";
}
};
#Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
#Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.watcher">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application
android:name=".MainApplication"
android:label="#string/app_name"
android:icon="#mipmap/ic_launcher"
android:allowBackup="false"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<service
android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service
android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
</application>
</manifest>
And the actual code that handles the notifications:
async checkPermission() {
const enabled = await firebase.messaging().hasPermission();
if (enabled) {
this.receiveToken();
} else {
this.requestPermission();
}
}
async receiveToken() {
let fcmToken = await AsyncStorage.getItem('fcmToken');
if (!fcmToken) {
fcmToken = await firebase.messaging().getToken();
if (fcmToken) {
await AsyncStorage.setItem('fcmToken', fcmToken);
}
}
return this.messageListener = firebase.messaging().onMessage(notif => {
try {
console.warn(JSON.stringify(notif));
} catch(e) {
console.warn("error trying to handle the message")
}
})
}
async requestPermission() {
try {
await firebase.messaging().requestPermission();
this.receiveToken();
} catch (error) {
console.warn('permission rejected');
}
}
componentDidMount() {
this.makeRemoteRequest();
this.checkPermission();
}
componentWillUnmount() {
this.messageListener();
}