Search code examples
flutter-webonesignalflutter-plugin

Is it possible to use OneSignal with Flutter-Web?


Calling any of the OneSignal functions works fine on android but on web it throws the following exception. Note that the subscription part is working, I had my appId set in the index.html, I uploaded the service worker js files and I received the first welcoming message.

But I need to be able to get the "playerId" and save it and also to be able to send notification from the web.

Example of OneSignal function calls:

  await OneSignal.shared.setAppId(kOneSignalAppId);
  OSDeviceState? deviceState = await OneSignal.shared.getDeviceState();
  OneSignal.shared.postNotification(notification);

Exception:

Error: MissingPluginException(No implementation found for method OneSignal#log on channel OneSignal)
at Object.throw_ [as throw] (http://localhost:49430/dart_sdk.js:5079:11)
at MethodChannel._invokeMethod (http://localhost:49430/packages/flutter/src/services/restoration.dart.lib.js:1526:21)
at _invokeMethod.next (<anonymous>)
at http://localhost:49430/dart_sdk.js:38749:33
at _RootZone.runUnary (http://localhost:49430/dart_sdk.js:38620:59)
at _FutureListener.thenAwait.handleValue (http://localhost:49430/dart_sdk.js:33820:29)
at handleValueCallback (http://localhost:49430/dart_sdk.js:34372:49)
at Function._propagateToListeners (http://localhost:49430/dart_sdk.js:34410:17)
at _Future.new.[_completeWithValue] (http://localhost:49430/dart_sdk.js:34258:23)
at async._AsyncCallbackEntry.new.callback (http://localhost:49430/dart_sdk.js:34279:35)
at Object._microtaskLoop (http://localhost:49430/dart_sdk.js:38887:13)
at _startMicrotaskLoop (http://localhost:49430/dart_sdk.js:38893:13)
at http://localhost:49430/dart_sdk.js:34626:9

Error: MissingPluginException(No implementation found for method OneSignal#setAppId on channel OneSignal)
at Object.throw_ [as throw] (http://localhost:49430/dart_sdk.js:5079:11)
at MethodChannel._invokeMethod (http://localhost:49430/packages/flutter/src/services/restoration.dart.lib.js:1526:21)
at _invokeMethod.next (<anonymous>)
at http://localhost:49430/dart_sdk.js:38749:33
at _RootZone.runUnary (http://localhost:49430/dart_sdk.js:38620:59)
at _FutureListener.thenAwait.handleValue (http://localhost:49430/dart_sdk.js:33820:29)
at handleValueCallback (http://localhost:49430/dart_sdk.js:34372:49)
at Function._propagateToListeners (http://localhost:49430/dart_sdk.js:34410:17)
at _Future.new.[_completeWithValue] (http://localhost:49430/dart_sdk.js:34258:23)
at async._AsyncCallbackEntry.new.callback (http://localhost:49430/dart_sdk.js:34279:35)
at Object._microtaskLoop (http://localhost:49430/dart_sdk.js:38887:13)
at _startMicrotaskLoop (http://localhost:49430/dart_sdk.js:38893:13)
at http://localhost:49430/dart_sdk.js:34626:9

Does it need a web plugin? Does a one exist or should I create one? If so could you please tell me how?


Solution

  • After a lot of investigation, I was able to confirm the following:

    1. The "Flutter SDK" is only for mobile apps, it doesn't work on web apps
    2. For web apps we should be using the "Web SDK" i.e. using Java Script not Flutter
    3. For both mobile & web we can use the REST API, but in most cases it will need app-id, player-id & API Key, so for initilaization & getting the player-id we will still need the "Flutter SDK" for mobile apps or the "Web SDK" for web This is how I managed to make it work on both mobile & web app with the same code: For Web part
    • Initializing via Web SDK
    • Getting the player-id via Web SDK
    • Saving player-id in a hidden element via javascipt
    • Retrieve the player-id from the Flutter code via "universal_html" package
    • Settting external-id via the REST API
    • Sending notification via REST API

    For mobile part

    • Initializing via Flutter SDK
    • Getting the player-id via Flutter SDK
    • Settting external-id via Flutter SDK
    • Sending notification via REST API (as Flutter SDK can't send to external id)

    index.html

    <head>
      ...
      <script src="https://cdn.onesignal.com/sdks/OneSignalSDK.js" async=""></script>
      <script>
        var OneSignal = window.OneSignal || [];
        var initConfig = {
          appId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
          persistNotification: true,
          notifyButton: {
              enable: true
          },
        };
        OneSignal.push(function () {
          OneSignal.init(initConfig);
        });
      </script>
    </head>
    <body>
      <input type="hidden" id="osUserID" value="ERR" />
      ...
      <script>
        var eUserID = document.getElementById('osUserID');
        function getUserId() {
          OneSignal.getUserId(function(osUserID) {
            if (eUserID && osUserID) {
              eUserID.value = osUserID;
              }
          });
        }
        OneSignal.push(function () {
          OneSignal.isPushNotificationsEnabled(function(isEnabled) {
            if (isEnabled) getUserId();
          });
          OneSignal.on('subscriptionChange', function (isSubscribed) {
            if (isSubscribed) getUserId();
          });
        });
      </script>
    </body>
    

    From inside Flutter

      import 'package:flutter/foundation.dart';
      import "package:universal_html/html.dart";
      import 'package:onesignal_flutter/onesignal_flutter.dart';
      import 'onesignal_api.dart';
      ...
      Future<String> getPlayerId(String exteralId) async {
        String _osUserID;
        if (kIsWeb){
          InputElement? element = querySelector("#osUserID") as InputElement?;
          osUserID = element?.value??"Not Subscribed";
          await _oneSignalAPI.setExternalId(osUserID, exteralId);
        }
        else {
          await OneSignal.shared.setAppId(kOneSignalAppId);
          OSDeviceState? deviceState = await OneSignal.shared.getDeviceState();
          osUserID = deviceState?.userId??"Not Subscribed";
          await OneSignal.shared.setExternalUserId(exteralId);
        }
        return osUserID;
      }
      Future<void> sendMessage(String exteralId, String message) async {
        _oneSignalAPI.sendNotification(exteralId, message);
      }