9

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');
});
Junsu Cho
  • 826
  • 7
  • 16
Ashutosh Sharma
  • 1,419
  • 2
  • 12
  • 22
  • I had the same problem with Firebase. Try putting this code in your `initState` of your Widget mounted after login. Then you will have access to `Navigator` with context – gaheinrichs Oct 23 '18 at 16:52
  • It is not working. At first time when the app is opened It worked. But when I close the app it stops working. It only opens the Home page – Ashutosh Sharma Oct 24 '18 at 05:25

5 Answers5

4

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

sjmcdowall
  • 1,471
  • 4
  • 15
  • 27
  • 4
    What is "globals.navigatorKey" Are we missing some code before getting here? – F-1 Feb 08 '19 at 11:14
  • That's a way to get a handle to the Navigation system -- the field is defined above .. and is set in MaterialApp code above as you can see. I don't think any code is missing. – sjmcdowall Feb 08 '19 at 13:46
  • same problem , I don't have named route and my page accepts one argument the how to do that. – Rakesh Roy Oct 17 '19 at 09:34
  • @sjmcdowall I used it, my code was working but it does not work when app is in background. It works when user is using the app and when it is close but does not work in background case. – Priyansh jain Jul 24 '21 at 12:35
  • Global state, this is actually a bad solution. Callback based solutions are much better. – Murtadha S. Feb 05 '22 at 12:50
1

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
Nimr Sawafta
  • 594
  • 6
  • 15
1

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);
      }
    });
  }
Dharman
  • 30,962
  • 25
  • 85
  • 135
Mr Special
  • 1,576
  • 1
  • 20
  • 33
1

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

afifi
  • 75
  • 1
  • 10
0

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);
    });
  } 
Gabriel Almeida
  • 426
  • 4
  • 7