I am working on a Flutter Application where I want to use the flutter_local_notifications with Firebase Cloud Messaging. When I added the following code to my main.dart-
#pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await setupFlutterNotifications();
showFlutterNotification(message);
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
print('Handling a background message ${message.messageId}');
}
/// Create a [AndroidNotificationChannel] for heads up notifications
late AndroidNotificationChannel channel;
bool isFlutterLocalNotificationsInitialized = false;
Future<void> setupFlutterNotifications() async {
if (isFlutterLocalNotificationsInitialized) {
return;
}
channel = const AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
'This channel is used for important notifications.', // description
importance: Importance.high,
);
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
/// Create an Android Notification Channel.
///
/// We use this channel in the `AndroidManifest.xml` file to override the
/// default FCM channel to enable heads up notifications.
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
/// Update the iOS foreground notification presentation options to allow
/// heads up notifications.
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
isFlutterLocalNotificationsInitialized = true;
}
void showFlutterNotification(RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null && !kIsWeb) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channel.description,
icon: 'launch_background',
),
),
);
}
}
/// Initialize the [FlutterLocalNotificationsPlugin] package.
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await Firebase.initializeApp();
// Set the background messaging handler early on, as a named top-level function
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
if (!kIsWeb) {
await setupFlutterNotifications();
}
I got an error-
android:exported needs to be explicitly specified for element <receiver#com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver>. Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined.
I read several posts and found out that I have to add android:exported="true" in AndroidManifest.xml. After adding-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.blog_app">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:label="blog_app"
android:name="${applicationName}"
android:icon="#mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
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">
<!-- 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"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</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 am still getting the same error after adding the exported property to the Manifest file. There is only MainActivity and no other component. Please help!
Did you check this ->
android:exported needs to be explicitly specified for element <receiver#com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver>
Flutter: Targeting S+ (version 31 and above) requires that an explicit value for android:exported be defined when intent filters are present]
there's also a video for same problem
https://www.youtube.com/watch?v=fma_umbAe6A
and a snippet
<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver"
android:exported="true">
</receiver>
Hope it helps you
Related
i have this issue when i tried to run my app in a physical device in debug mode.
When I try to debug the next code:
class SocketService with ChangeNotifier {
ServerStatus _serverStatus = ServerStatus.Connecting;
SocketService() {
this._initConfig();
}
void _initConfig() async {
IO.Socket socket = IO.io('http://192.168.56.1:3000/', <String, dynamic>{
'transports': ['websocket'],
'autoConnect': true,
});
socket.on('connect', (_) {
print('connect');
});
socket.on('event', (data) => print(data));
socket.on('disconnect', (_) => print('disconnect'));
socket.on('fromServer', (_) => print(_));
}
}
And in my backend i have this:
file index.js
const express = require('express');
const path = require('path');
require('dotenv').config();
const app = express();
const server = require('http').createServer(app);
module.exports.io = require('socket.io')(server);
require('./sockets/socket');
const publicPath = path.resolve( __dirname, 'public' );
app.use( express.static( publicPath ) );
server.listen( process.env.PORT, ( err ) => {
if ( err ) throw new Error(err);
console.log('Servidor corriendo en puerto', process.env.PORT );
});
file socket.js
const { io } = require('../index');
io.on('connection', client => {
console.log('Cliente conectado');
client.on('disconnect', () => {
console.log('Cliente desconectado');
});
client.on('mensaje', ( payload ) => {
console.log('Mensaje', payload);
io.emit( 'mensaje', { admin: 'Nuevo mensaje' } );
});
});
In web works good and print's the 'disconnect' adn the 'connect' perfectly, but when i tried to debug in my cellphone, nothings happends, i don't get any message from the socket instance, if anyone knows how to solved this I would appreciate.
You can upload your code into the Heroku server or you can make a ngrok URL to connect the socket with a physical device.
I figure out what was the problem, was with the AndroidManifest
The way I solved was with the next code
In the <application> I add this line android:usesCleartextTraffic="true"
The result was this:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.band_names">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="band_names"
android:icon="#mipmap/ic_launcher"
android:usesCleartextTraffic="true">
<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">
<!-- 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>
</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>
I'm trying to log the user into a Google API from my Flutter app, but can't get it to automatically fetch the token. The closest I got was to see the token string in the auth screen and be asked to copy/paste it back into the app. I suspect it's related to the redirect_uri parameter.
I attempted with both oauth2_client and flutter_appauth and it's pretty much the same outcome. When setting up the client, if I use the first redirect_uri provided by Google urn:ietf:wg:oauth:2.0:oob, after granting the permissions it shows the token in the auth screen and instructs the user to copy it and paste it back in the app. If I use the uri that I set in AndroidManifest.xml and build.gradle, instead of the consent screen, I get this message in the browser:
"Invalid parameter value for redirect_url: Missing scheme:
ai.autonet.afterme"
Lastly, if I use "http://localhost" (the second uri provided by Google), I get "request timed out".
My client configuration from Google's side looks like this:
"client_id":"somethingsomething.apps.googleusercontent.com","project_id":"afterme-850af","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]
Here is the simplest version of the flutter_appauth implementation:
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_appauth/flutter_appauth.dart';
import 'package:oauth2_client/access_token_response.dart';
import 'package:http/http.dart' as http;
const AUTH_ENDIPOINT = 'https://accounts.google.com/o/oauth2/auth';
const CLIENT_ID =
'somethingsomething.apps.googleusercontent.com';
const REDIRECT_URI = 'ai.autonet.afterme';
const TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token";
var scopes = [
"https://www.googleapis.com/auth/youtube",
"https://www.googleapis.com/auth/youtube.upload",
];
void main() {
runApp(MyApp());
}
var httpClient = http.Client();
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
FlutterAppAuth appAuth = FlutterAppAuth();
authorize() async {
final AuthorizationTokenResponse result =
await appAuth.authorizeAndExchangeCode(AuthorizationTokenRequest(
CLIENT_ID, REDIRECT_URI,
serviceConfiguration: AuthorizationServiceConfiguration(
AUTH_ENDPOINT,
TOKEN_ENDPOINT),
scopes: scopes));
print(result.accessToken.toString());
}
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
getResources() async {
http.Response resp = await httpClient
.get('GET https://www.googleapis.com/youtube/v3/videos');
print(resp.body);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text("Permission"), onPressed: () => widget.authorize()),
],
),
),
);
}
}
----------------------------------------------------------------
AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ai.autonet.afterme">
<!-- 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"
android:label="afterme"
android:icon="#mipmap/ic_launcher">
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" >
<intent-filter android:label="flutter_web_auth">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="ai.autonet.afterme" />
</intent-filter>
</activity>
<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">
<!-- 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>
</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>
-------------------------------------------------------------------------------------
build.gradle:
...
defaultConfig {
applicationId "ai.autonet.afterme"
minSdkVersion 18
targetSdkVersion 29
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
manifestPlaceholders = [
'appAuthRedirectScheme': 'ai.autonet.afterme'
]
}
...
Any help would be valued.
Have you tried adding :// to the end of your Scheme? I'm not sure you are providing a valid scheme name to Google.
Can you try ai.autonet.afterme:// or ai.autonet.afterme://auth?
The correct way to set redirect_uri in flutter for both iOS and android is as follows:
Step one
Android - under android/app/build.gradle
defaultConfig {
applicationId "com.testingapp" // Set the applicationId
minSdkVersion 18
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
manifestPlaceholders = [
'appAuthRedirectScheme': 'com.testingapp' // Add this also using your application ID URI as the based for all your derived URi
]
}
iOS - ios/Runner/Info.plist
<dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.testingapp</string> //Again Application Id should be added here
</array>
</dict>
</array>
...
Then in your app you can use it as follows:
const AUTH0_REDIRECT_URI = "com.testingapp://login-callback";
please note the "://login-callback" is added to the application ID here, its not defined anywhere else.
Finally in your provider add it as well. In my case I am using keycloak.
enter image description here
Flutter firebase background notification I pushed the image notification but it's showing only text but if app open it's working fine but app terminated or minimize it's showing only default notification I tried everything but it doesn't work I referred this also https://pub.dev/packages/firebase_messaging no use.
check this image
background notification not showing that image?
Application.kt
package YOUR PACKAGE
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 com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin
class Application : FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate()
FlutterFirebaseMessagingService.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("com.dexterous.flutterlocalnotifications")) {
FlutterLocalNotificationsPlugin.registerWith(registry!!.registrarFor("com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin"));
}
}
}
MainActivity.kt
package YOUR PACKAGE
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="YOUR PACKAGE"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:name=".Application"
android:label="APP_NAME"
android:icon="#drawable/home_logo"
tools:replace="android:label">
<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">
<!-- 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>
<!-- Add below to ensure we get the payload when tapping on a notification -->
<intent-filter>
<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="com.google.firebase.messaging.default_notification_channel_id"
android:value="#string/default_notification_channel_id"/>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
main.dart
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print('on message $message');
showNotification(message);
},
onBackgroundMessage: Platform.isIOS
? null
: myBackgroundMessageHandler,
onResume: (Map<String, dynamic> message) async {
print('on resume $message');
showNotification(message);
},
onLaunch: (Map<String, dynamic> message) async {
print('on launch $message');
showNotification(message);
},
);
myBackgroundMessageHandler method in same main.dart
static Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) {
final notification = message['notification'];
final data = message['data'];
print(notification);
}
Firebase send notification json Rest API
#header:
Content-Type:application/json
Authorization:key=YOUR FIREBASE SERVER KEY
#body -> raw
{
"notification":{
"title":"Plan Expired",
"body":"Your plan has expired please upgrade your plan today"
},
"data": {
"image":"https://i.ytimg.com/vi/zZ72Ujn8Rfw/maxresdefault.jpg"
},
"to":"NOTIFICATION TOKEN"
}
To show images you may need to use image tag inside notification in your Firebase send notification json Rest API. The below code worked for me.
{
"notification":{
"title":"Plan Expired",
"body":"Your plan has expired please upgrade your plan today",
"image":"https://imgsv.imaging.nikon.com/lineup/dslr/df/img/sample/img_01.jpg"
}
"to":"NOTIFICATION_TOKEN"
}
I am using firebase_messaging: ^5.0.1 package to achieve push notifications, everything is working fine in IOS whereas coming to the android when my mobile application running background I am receiving a notification but it is not navigating to the respective screens, it just opens the default screen. How to achieve navigation to that particular screen.
PS: I implemented click_action functionality that's the reason it's working fine in iOS but Android it shows the below message
W/FirebaseMessaging( 8260): Missing Default Notification Channel metadata in AndroidManifest. Default value will be used.
Here is my AndroidManifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.check">
<!-- 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"
android:label="Cargill FC"
android:icon="#mipmap/ic_launcher">
<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:allowBackup="false"
android:fullBackupContent="false"
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" />
<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.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Push notificatio code:
#override
void initState() {
super.initState();
tabController = new TabController(length: 2, vsync: this);
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
onFirebaseMessage(message);
},
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");
});
_firebaseMessaging.getToken().then(registerFirebaseTokenForUser);
}
Here onMessage is the only thing working perfectly in Android. I want to achieve the same when it is running background.
For those who are not able to find "string.xml", you can find it under: android>app>src>main>res>values. It is not the same as styles.xml. If you do not have one yet, you can create one:
Right click "values" folder,
Click New/Values Resource File
Copy, and paste the following text:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="default_notification_channel_id" translatable="false">fcm_default_channel</string>
</resources>
Maksim has a pretty solid answer here including links to the official docs. You need to add a the following meta-data tag in you Manifest:
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="#string/default_notification_channel_id"/>
And in string.xml you can declare default_notification_channel_id in the following way:
<string name=“default_notification_channel_id”>Channel ID</string>
Then you must provide an attribute with that specific id when sending push notifications.
EDIT
It is possible to have multiple meta-data tags in your AndroidManifest.xml:
<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"/>
Adding FLUTTER_NOTIFICATION_CLICK is required to be sent, for onResume and onLunch to be executed.
{
"notification": {...},
"click_action": "FLUTTER_NOTIFICATION_CLICK"
}
For my golang server, this meant adding the AndroidConfig
message := &messaging.Message{
Topic: topic,
Notification: &messaging.Notification{/* */}
Data: data,
APNS: &messaging.APNSConfig{/* */}
Android: &messaging.AndroidConfig{
Notification: &messaging.AndroidNotification{
ClickAction: "FLUTTER_NOTIFICATION_CLICK",
},
},
}
1- At first, add this meta code after </activity> tag in AndroidManifest.xml which located in path <flutter project path>/android/app/src/main/AndroidManifest.xml
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="#string/notification_channel_id" />
Note: If you set this meta inside <activity> the code will not work.
2- Modify file (or create new file if not exists) in this path <flutter project path>/android/app/src/main/res/values/string.xml to be like this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="default_notification_channel_id" translatable="false">fcm_default_channel</string>
</resources>
This will solve the problem Missing Default Notification Channel metadata in AndroidManifest. Default value will be used.
But after that, you need to create this channel in Android, to do that go to file <flutter project path>//android/app/src/main/kotlin/com/examble/project_name/Application.kt and add this function:
private fun createChannel(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel
val name = getString(R.string.default_notification_channel_id)
val channel = NotificationChannel(name, "default", NotificationManager.IMPORTANCE_HIGH)
val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
Then call it from onCreate() function:
override fun onCreate() {
super.onCreate()
createChannel()
.........
}
Adding 'click_action': 'FLUTTER_NOTIFICATION_CLICK' to my notification's data solved this for me
If your flutter version is greater than 1.12 you don't need to create any file like Application.java or Application.kt just add the below meta value to you AndroidManifest file
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="high_importance_channel" />
Reference: https://firebase.flutter.dev/docs/messaging/overview/
I'm developing a NativeScript app which involves push notifications.Whenever push notification came I need to store notification contents into database.
For that I have written some code in "onMessageReceived" function.This code is in the page in which GCM registration code is there.
If app is running then every thing working fine. The problem is if app is closed then "onMessageReceived" function is not even executing(I checked with console logs).
So for that I'm trying to place "onMessageReceived" function in app.js so that even app is closed it will execute.For that I'm trying to import the "nativescript-push-notifications" in app.js, but getting error saying "application is null,it's not passed correctly".Below is my app.js code.
app.js
var application = require("application");
var gcm=require("nativescript-push-notifications");
if(gcm.onMessageReceived) {
gcm.onMessageReceived(function callback(data) {
console.log("message received:::: ", "" + JSON.stringify(data));
storeInDatabase(data);// some function to store notification content into db.
});
}
application.mainModule="main-page";
application.start({ moduleName: "main-page" });
Can we import "nativescript-push-notifications" reference in app.js??
Any suggestions will be helpful.Thanks.
Application is null because your app has not started yet try adding the plugin in the application launch event
var application = require("application");
application.start({ moduleName: "main-page" });
application.on(application.launchEvent, function (args) {
if (args.android) {
var gcm = require("nativescript-push-notifications");
gcm.register({ senderID: 'conversate-1148' }, function (data) {
self.set("message", "" + JSON.stringify(data));
}, function () { });
if (gcm.onMessageReceived) {
gcm.onMessageReceived(function callback(data) {
console.log("message received:::: ", "" + JSON.stringify(data));
storeInDatabase(data);// some function to store notification content into db.
});
}
} else if (args.ios !== undefined) {
//Do ios stuff here
}
});
In addition to Osei's code you might want to check out your AndroidManifest.xml file (generated in your platforms/android folder) and make sure the following permissions are set :
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
and also that the plugin is registered as a service in the same AndroidManifest.xml file as follows:
<activity android:name="com.telerik.pushplugin.PushHandlerActivity"/>
<receiver android:name="com.google.android.gms.gcm.GcmReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.pushApp.gcm" />
</intent-filter>
</receiver>
<service android:name="com.telerik.pushplugin.PushPlugin" android:exported="false" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>