How do I open a specific page on onesignal notification click on flutter?

Viewed 8066

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');
});
5 Answers

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 :)

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);
      }
    });
  }

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, )

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

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);
    });
  } 
Related