Reputation: 1549
I am using OneSignal push notification service and I want to open the app directly to specific page on notification click. I am sending the page through data. I tried navigator.push but it didn't work i guess because of context issue. I am calling _initializeonesignal() after login which contains onesignal init and the following code.
OneSignal.shared.setNotificationOpenedHandler((notification) {
var notify = notification.notification.payload.additionalData;
if (notify["type"] == "message") {
//open DM(user: notify["id"])
}
if (notify["type"] == "user") {
//open Profileo(notify["id"])
}
if (notify["type"] == "post") {
//open ViewPost(notify["id"])
}
print('Opened');
});
Upvotes: 10
Views: 14314
Reputation: 95
You can use this Code:
final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
OneSignal.shared.setNotificationOpenedHandler((result) {
navigatorKey.currentState.push(
MaterialPageRoute(
builder: (context) => YourPage(),
),
);
});
MaterialApp(
home: SplashScreen(),
navigatorKey: navigatorKey,
)
Upvotes: 3
Reputation: 291
Solution with riverpods
main:
class MyApp extends ConsumerStatefulWidget {
const MyApp({super.key});
@override
ConsumerState<MyApp> createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
bool requireConsent = false;
@override
void initState() {
super.initState();
OneSignalWapper.forgroundListener(ref);
}
@override
Widget build(BuildContext context) {
print('User: ${ref.watch(userDataProvider)?.data?.email}');
print(
'Role: ${Roles.roleChecker(ref.read(userDataProvider)?.data?.roles) ?? ""}');
print(
'Token: ${ref.watch(sharedPreferencesProvider)?.getString(SharedPreferencesNames.token.name)}');
print('User ID: ${ref.watch(userDataProvider)?.data?.id}');
print(
"device id: ${ref.watch(sharedPreferencesProvider)?.getString(SharedPreferencesNames.deviceid.name)}");
var router = ref.read(routing);
//Called here......................
OneSignalWapper.handleClickNotification(ref);
return MaterialApp.router(
debugShowCheckedModeBanner: false,
themeMode: ref.watch(themeProvider),
theme: MyThemes.lightTheme,
darkTheme: MyThemes.darkTheme,
title: 'Flutter Demo',
routeInformationParser: router.routeInformationParser,
routerDelegate: router.routerDelegate,
routeInformationProvider: router.routeInformationProvider,
);
}
}
custom wrapper file...
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:onesignal_flutter/onesignal_flutter.dart';
import 'package:rescue/utils/constants.dart';
import 'package:rescue/utils/provider.dart';
import 'package:rescue/utils/routing/router.dart';
import 'package:rescue/utils/routing/router_names.dart';
class OneSignalWapper {
static void handleClickNotification(WidgetRef ref) {
OneSignal.Notifications.addClickListener((event) async {
var router = ref.read(routing);
ref
.read(notificationList.notifier)
.setID(ref.watch(userDataProvider)!.data!.id!);
await ref
.watch(notificationList.notifier)
.getUsersAnnoucements(ref.watch(userDataProvider)?.data?.id);
switch (ref.watch(userDataProvider)?.data?.accountType) {
case Roles.user:
router.goNamed(RouteNames.userPushNotifications.name);
break;
case Roles.admin:
router.goNamed(RouteNames.adminHomePage.name);
break;
case Roles.assistant:
router.goNamed(RouteNames.assistantHomePage.name);
break;
default:
router.goNamed(RouteNames.login.name);
}
});
}
// static void forgroundListener(WidgetRef ref) {
// OneSignal.Notifications.addForegroundWillDisplayListener((event) async {
// ref
// .read(notificationList.notifier)
// .setID(ref.watch(userDataProvider)!.data!.id!);
// await ref
// .watch(notificationList.notifier)
// .getUsersAnnoucements(ref.watch(userDataProvider)!.data!.id, true);
// print(event.notification.title);
// });
// }
}
Upvotes: 0
Reputation: 455
I find the solution:
On your home screen, set the handler. And, before this, set on your configuration notification this way
First:
Map<String, dynamic> additional = {
"route": 'detail',
"userId": widget.userId
};
await OneSignal.shared.postNotification(OSCreateNotification(
playerIds: userToken,
content: 'your content',
heading: 'your heading',
additionalData: additional,
androidLargeIcon:'any icon'));
Second:
OneSignal.shared.setNotificationOpenedHandler(
(OSNotificationOpenedResult action) async {
Map<String, dynamic> dataNotification =
action.notification.payload.additionalData;
if (dataNotification.containsValue('detailPage')) {
await Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new DetailScreen(
userId: dataNotification['userId'],
),
).catchError((onError) {
print(onError);
});
}
Upvotes: 0
Reputation: 1676
I resolved the same problems, as below:
In the main screen file MyApp.dart
@override
void initState() {
OneSignalWapper.handleClickNotification(context);
}
OneSignalWapper.dart :
static void handleClickNotification(BuildContext context) {
OneSignal.shared
.setNotificationOpenedHandler((OSNotificationOpenedResult result) async {
try {
var id = await result.notification.payload.additionalData["data_id"];
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => PostDetailsScreen.newInstance('$id')));
} catch (e, stacktrace) {
log(e);
}
});
}
Upvotes: 1
Reputation: 647
It's simple, by using onesignal, you can create system call from kotlin to flutter
In my case, I had to take the data in the URL from a notification that comes from onesignal in WordPress:
package packageName.com
import android.os.Bundle
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
// import io.flutter.plugins.firebaseadmob.FirebaseAdMobPlugin;
private val CHANNEL = "poc.deeplink.flutter.dev/channel"
private var startString: String? = null
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
MethodChannel(flutterEngine.dartExecutor, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "initialLink") {
if (startString != null) {
result.success(startString)
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = getIntent()
startString = intent.data?.toString()
}
}
This I'm taking data from onCreate, yet only when clicking on the notification, I will take the "intent" data and then I will send it to my flutter code in the following class:
import 'dart:async';
import 'package:flutter/services.dart';
class MyNotificationHandler {
//Method channel creation
static const platform =
const MethodChannel('poc.deeplink.flutter.dev/channel');
//Method channel creation
static String url;
static String postID;
static onRedirected(String uri) {
url = uri;
postID = url.split('/').toList()[3];
}
static Future<String> startUri() async {
try {
return platform.invokeMethod('initialLink');
} on PlatformException catch (e) {
return "Failed to Invoke: '${e.message}'.";
}
}
//Adding the listener into contructor
MyNotificationHandler() {
//Checking application start by deep link
startUri().then(onRedirected);
}
}
Here I'm taking data from a WordPress URL, the last word after the 4ed '/' which is the id of the post.
now how to use it and call it, as I created it static I will use it in my code when the first page loads,
import 'package:com/config/LocalNotification.dart';
class MyLoadingPage extends StatefulWidget {
MyLoadingPage() {
MyNotificationHandler.startUri().then(MyNotificationHandler.onRedirected);
}
@override
_MyLoadingPageState createState() => _MyLoadingPageState();
}
...
This page will load the data from my WordPress API.
so after loading the data from the database, I will check if a value of the id, and navigate to the article page, the example in my home page:
....
@override
void initState() {
MyViewWidgets.generalScaffoldKey = _scaffoldKey;
myWidgetPosts = MyPostsOnTheWall(MyPost.allMyPosts, loadingHandler);
MyHomePAge.myState = this;
super.initState();
if (MyNotificationHandler.postID != null) {
Future.delayed(Duration(milliseconds: 250)).then((value) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MyArticlePage(MyPost.allMyPosts
.firstWhere((element) =>
element.id == MyNotificationHandler.postID))));
});
}
}
....
The secrete is in kotlin or Java by using that call from kotlin to fluter or from java to flutter, I think you will have to do the same with ios, I will leave an article that helped me.
https://medium.com/flutter-community/deep-links-and-flutter-applications-how-to-handle-them-properly-8c9865af9283
Upvotes: 1
Reputation: 1631
You will need to register a global Navigator handle in your main application scaffold -- then you can use it in your notification handlers..
So -- in our app in our main App we have :
// Initialize our global NavigatorKey
globals.navigatorKey = GlobalKey<NavigatorState>();
...
return MaterialApp(
title: 'MissionMode Mobile',
theme: theme,
initialRoute: _initialRoute,
onGenerateRoute: globals.router.generator,
navigatorKey: globals.navigatorKey,
);
The key is the navigatorKey: part and saving it to somewhere you can access somewhere else ..
Then in your handler:
OneSignal.shared.setNotificationOpenedHandler(_handleNotificationOpened); ...
// What to do when the user opens/taps on a notification
void _handleNotificationOpened(OSNotificationOpenedResult result) {
print('[notification_service - _handleNotificationOpened()');
print(
"Opened notification: ${result.notification.jsonRepresentation().replaceAll("\\n", "\n")}");
// Since the only thing we can get current are new Alerts -- go to the Alert screen
globals.navigatorKey.currentState.pushNamed('/home');
}
That should do the trick -- does for us anyway :)
Upvotes: 4