amplitude/Amplitude-Flutter

Flutter Web Support

ibrahim-mubarak opened this issue ยท 11 comments

How can we get started to use this on Flutter Web?

Hey, we don't have the plan to support web for Flutter SDK atm. Any chance you can use our JS SDK?

It's not a pressing matter for us at the moment as we are still testing out what works and doesn't in Flutter web.
I suppose this means that we have to create a platform plugin using the JS Sdk?

I would also be keen to see this web support in the Flutter SDK. At the moment I have built an integration using the HTTP v2 API, but it's not ideal. I have conditional import logic to import the android/iOS module if running on mobile, otherwise use the HTTP API if not.

We'd also have a need for flutter web support. Use of JS SDK is close to the same thing as building web support ourselves. (because we'd have to build JS mappings and initialisation into a flutter library)

Yes, please add web support. As others have mentioned, it's not a trivial task to use the JS SDK instead. I plan to just turn off Amplitude when running on web until proper support is added.

Flutter now supports other platforms such as MacOS and others such as Windows are coming soon. It would be great if the plugin could support these as well. Worth considering when you add web support to keep it generic enough to support these platforms as well.

Our Flutter app Sharezone is available as an Android, iOS, Web and macOS app. We would love to see Flutter Web support โค๏ธ Flutter Web is now stable, so many other Flutter Developers will need this in the near future.

I would also like flutter web support, or at least a timeline of when it will be implemented? I have to strip amplitude out of my app and replace it with something else otherwise.

You can use js interop for dart.
Unfortunately, I don't have free time to make a PR, so I'll add the integration below.

amplitude_web.dart

@JS('amplitude')
library amplitude;

import 'package:js/js.dart';
import 'package:js/js_util.dart' as util;

class AmplitudeWeb {
  final _Amplitude _amplitude = getInstance();

  void init(
    String apiKey, {
    String? optUserId,
    Map<String, dynamic>? optConfig,
    Function? optCallback,
  }) =>
      _amplitude.init(
        apiKey,
        optUserId,
        _mapToJsObject(optConfig),
        optCallback,
      );

  void logEvent(
    String name, {
    Map<String, String>? params,
    Function? optCallback,
  }) =>
      _amplitude.logEvent(
        name,
        _mapToJsObject(params),
        optCallback,
      );

  void setOptOut(bool enable) => _amplitude.setOptOut(enable);

  void setUserProperties(Map<String, String> properties) =>
      _amplitude.setUserProperties(_mapToJsObject(properties));
}

external _Amplitude getInstance();

@JS('Amplitude')
class _Amplitude {
  external void init(String apiKey, String? optUserId, Object? optConfig, Function? optCallback);

  external void logEvent(String name, Object? params, Function? optCallback);

  external void setOptOut(bool enable);

  external void setUserProperties(Object properties);
}

dynamic _mapToJsObject(Object? dartObject) {
  if (_isPrimitive(dartObject)) {
    return dartObject;
  }
  if (dartObject is Map) {
    final jsMap = util.newObject();
    dartObject.forEach((key, value) {
      util.setProperty(jsMap, key, _mapToJsObject(value));
    });
    return jsMap;
  }
  throw Exception('Unknpown type of object: $dartObject');
}

bool _isPrimitive(Object? value) {
  if (value == null || value is num || value is bool || value is String) {
    return true;
  }
  return false;
}

amplitude_mobile_stub.dart

class AmplitudeWeb {
  void init(String apiKey,
      {String? optUserId, Map<String, dynamic>? optConfig, Function? optCallback}) {
    throw UnsupportedError('Haven\'t implementation');
  }

  void logEvent(String name, {Map<String, String>? params, Function? optCallback}) {
    throw UnsupportedError('Haven\'t implementation');
  }

  void setOptOut(bool enable) {
    throw UnsupportedError('Haven\'t implementation');
  }

  void setUserProperties(Map properties) {
    throw UnsupportedError('Haven\'t implementation');
  }
}

amplitude.dart

import 'amplitude_mobile_stub.dart'
    if (dart.library.html) 'amplitude_web.dart'
    if (dart.library.io) 'amplitude_mobile_stub.dart';

class Amplitude {
  Amplitude._();

  final AmplitudeWeb _amplitudeWeb = AmplitudeWeb();

  static Amplitude getInstance() => Amplitude._();

  void init(
    String apiKey,
    String? optUserId,
    Map<String, dynamic>? optConfig,
  ) =>
      _amplitudeWeb.init(
        apiKey,
        optUserId: optUserId,
        optConfig: optConfig,
      );

  void logEvent(
    String name,
    Map<String, String>? params,
  ) =>
      _amplitudeWeb.logEvent(
        name,
        params: params,
      );

  void setOptOut(bool enable) => _amplitudeWeb.setOptOut(enable);

  void setUserProperties(Map<String, String> properties) =>
      _amplitudeWeb.setUserProperties(properties);
}

Conditional imports are required to work correctly on mobile platforms.

You also need to connect the web sdk to the index.html:

  <script type="text/javascript">
    (function (e, t) {
      var n = e.amplitude || { _q: [], _iq: {} }; var r = t.createElement("script")
        ; r.type = "text/javascript"
        ; r.integrity = "sha384-9jIJQlEp6cEk6foaEO/N5yzaJmQbTtugbpg9l8P/javk55WlPh7fJGR1vHhyDjON"
        ; r.crossOrigin = "anonymous"; r.async = true
        ; r.src = "https://cdn.amplitude.com/libs/amplitude-8.4.0-min.gz.js"
        ; r.onload = function () {
          if (!e.amplitude.runQueuedFunctions) {
            console.log("[Amplitude] Error: could not load SDK")
          }
        }
        ; var i = t.getElementsByTagName("script")[0]; i.parentNode.insertBefore(r, i)
        ; function s(e, t) {
          e.prototype[t] = function () {
            this._q.push([t].concat(Array.prototype.slice.call(arguments, 0))); return this
          }
        }
      var o = function () { this._q = []; return this }
        ; var a = ["add", "append", "clearAll", "prepend", "set", "setOnce", "unset", "preInsert", "postInsert", "remove"]
        ; for (var c = 0; c < a.length; c++) { s(o, a[c]) } n.Identify = o; var u = function () {
          this._q = []
            ; return this
        }
        ; var l = ["setProductId", "setQuantity", "setPrice", "setRevenueType", "setEventProperties"]
        ; for (var p = 0; p < l.length; p++) { s(u, l[p]) } n.Revenue = u
        ; var d = ["init", "logEvent", "logRevenue", "setUserId", "setUserProperties", "setOptOut", "setVersionName", "setDomain", "setDeviceId", "enableTracking", "setGlobalUserProperties", "identify", "clearUserProperties", "setGroup", "logRevenueV2", "regenerateDeviceId", "groupIdentify", "onInit", "logEventWithTimestamp", "logEventWithGroups", "setSessionId", "resetSessionId"]
        ; function v(e) {
          function t(t) {
            e[t] = function () {
              e._q.push([t].concat(Array.prototype.slice.call(arguments, 0)))
            }
          }
          for (var n = 0; n < d.length; n++) { t(d[n]) }
        } v(n); n.getInstance = function (e) {
          e = (!e || e.length === 0 ? "$default_instance" : e).toLowerCase()
            ; if (!Object.prototype.hasOwnProperty.call(n._iq, e)) {
              n._iq[e] = { _q: [] }; v(n._iq[e])
            } return n._iq[e]
        }; e.amplitude = n
    })(window, document);
  </script>

We start to support flutter web from Amplitude-Flutter v3.8.0. Thanks all for being interested in Amplitude Flutter. Please try it out and check the example app or go to Amplitude developer center to see the usage(https://developers.amplitude.com/docs/flutter-setup#flutter-web-support).

In case it helps anybody: I found that amplitude_flutter v3.8.0 would work fine if I ran my project locally in (Android Studio), but would fail to log events back to Amplitude if I did a flutter build web and hosted the built site on our server.

The good news is that 3.8.1 seems to have resolved this, and now it seems to be working fine in both cases.

In case it helps anybody: I found that amplitude_flutter v3.8.0 would work fine if I ran my project locally in (Android Studio), but would fail to log events back to Amplitude if I did a flutter build web and hosted the built site on our server.

The good news is that 3.8.1 seems to have resolved this, and now it seems to be working fine in both cases.

I am experiencing this problem with both version 3.8.1 and 3.15 .