Ashutosh Sharma
Ashutosh Sharma

Reputation: 1549

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

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

Answers (6)

afifi
afifi

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

mc100p
mc100p

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

Gabriel Almeida
Gabriel Almeida

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

Mr Special
Mr Special

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

Nimr Sawafta
Nimr Sawafta

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

sjmcdowall
sjmcdowall

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

Related Questions