Flutter WebView Bridge

This guide explains how to use WebEngage's JavaScript Bridge to enable SDK tracking from within a WebView in your Flutter applications. By using this bridge, web content inside a WebView can communicate directly with the Flutter app to trigger events like login, logout, tracking, and screen views

How It Works

The bridge acts as a communication layer between your web content and the native Flutter SDK:

Trigger: JavaScript within the WebView calls standard WebEngage methods such as webengage.user.login() or webengage.track().

Forward: The bridge intercepts these calls and forwards the data to the native Flutter layer.

Handle: The WebEngage Flutter SDK receives these forwarded events and processes them accordingly.

📘

Prerequisites :

  • webengage_flutter : 1.7.0 or Above

Supported WebView Plugins :

  • webview_flutter
  • flutter_inappwebview

Setup Instructions

Step 1 : Include the JS Bridge in Your Web Project

Create a file named webengage_flutter_bridge.js in your web project and add the code to define the bridge handlers

/**
 * Checks if `webview_flutter` bridge is available.
 * @returns {boolean} True if the webview_flutter channel is available.
 */
function isWebViewFlutterAvailable() {
    return (
        typeof window.webengage_flutter !== "undefined" &&
        typeof window.webengage_flutter.postMessage === "function"
    );
}

/**
 * Checks if `flutter_inappwebview` bridge is available.
 * @returns {boolean} True if the inappwebview handler is available.
 */
function isInAppWebViewAvailable() {
    return typeof window.flutter_inappwebview !== "undefined";
}

// ====================================
// Constants
// ====================================
const CHANNEL_NAME = "webengage_flutter";

const METHOD_LOGIN = "login";
const METHOD_LOGOUT = "logout";
const METHOD_SET_ATTRIBUTE = "setAttribute";
const METHOD_SCREEN = "screen";
const METHOD_TRACK_EVENT = "trackEvent";

const OBJECT_TYPE = "[object Object]";

// ====================================
// Initialization for `webview_flutter`
// ====================================

/**
 * Initializes the WebEngage bridge for `webview_flutter`.
 * Sets up function bindings and message handler.
 */
function initializeWebViewFlutterBridge() {
    const sendToWebViewFlutter = function (method, ...args) {
        const payload = JSON.stringify({ method, args });
        window.webengage_flutter.postMessage(payload);
    };
    initWebEngageBridge(sendToWebViewFlutter);
}

// ====================================
// Initialization for `flutter_inappwebview`
// ====================================

/**
 * Initializes the WebEngage bridge for `flutter_inappwebview`.
 * Sets up handler callbacks for native communication.
 */
function initializeInAppWebViewBridge() {
    const sendToInAppWebView = function (method, ...args) {
        window.flutter_inappwebview.callHandler(CHANNEL_NAME, method, ...args);
    };
    initWebEngageBridge(sendToInAppWebView);
}

// ====================================
// Common Bridge Logic
// ====================================

/**
 * Initializes the core WebEngage bridge by attaching WebEngage methods
 * and routing method calls to the native layer using the provided sender.
 *
 * @param {function} sendFunction - Function to dispatch method + args to native side.
 */
function initWebEngageBridge(sendFunction) {
    const type = Object.prototype.toString;

    // Setup global namespace if not already defined
    const we = window.webengage || (window.webengage = {});
    const user = (we.user = we.user || {});

    /**
     * Logs in or identifies the user by user ID.
     * @param {string} id - Unique user ID.
     */
    user.login = user.identify = function (id) {
        sendFunction(METHOD_LOGIN, id);
    };

    /**
     * Logs out the current user.
     */
    user.logout = function () {
        sendFunction(METHOD_LOGOUT, {});
    };

    /**
     * Sets user attributes (single or multiple).
     * @param {string|object} name - Attribute name or object of key-value pairs.
     * @param {*} [value] - Value if name is a string.
     */
    user.setAttribute = function (name, value) {
        let attr = {};
        if (type.call(name) === OBJECT_TYPE) {
            attr = name;
        } else {
            attr[name] = value;
        }
        sendFunction(METHOD_SET_ATTRIBUTE, attr);
    };

    /**
     * Tracks screen navigation within the app.
     * @param {string|object} name - Screen name or data.
     * @param {object} [data] - Optional metadata.
     */
    we.screen = function (name, data) {
        if (arguments.length === 1 && type.call(name) === OBJECT_TYPE) {
            data = name;
            name = null;
        }
        sendFunction(
            METHOD_SCREEN,
            name || null,
            type.call(data) === OBJECT_TYPE ? data : null
        );
    };

    /**
     * Tracks a custom event with optional metadata.
     * @param {string} name - Event name.
     * @param {object} [data] - Optional metadata.
     */
    we.track = function (name, data) {
        sendFunction(
            METHOD_TRACK_EVENT,
            name,
            type.call(data) === OBJECT_TYPE ? data : null
        );
    };

    console.log("WebEngage Flutter bridge initialized");
}

Step 2 : Embed the Script in HTML

Ensure you embed the webengage_flutter_bridge.js before the main WebEngage SDK (webengage.js):

<!-- MUST BE INCLUDED FIRST -->
<script src="webengage_flutter_bridge.js"></script>

<!-- Then include the main WebEngage SDK -->
<script src="webengage.js"></script>
📘

You must embed the webengage_flutter_bridge.js before the main WebEngage SDK (webengage.js). This allows the bridge to override the default SDK behavior for WebViews.

Step 3 : Update Instructions for webengage.js

To integrate WebEngage correctly with Flutter WebViews, update your webengage.js as shown below.

Implementation by Plugin
Choose the plugin you are currently using in your Flutter project for the setup instructions.

webview_flutter :
if (isWebViewFlutterAvailable()) {
  console.log("Detected webview_flutter. Initializing bridge...");
  initializeWebViewFlutterBridge();
} else {
  // fallback to default Web SDK initialization
}
flutter_inappwebview :
if (isInAppWebViewAvailable()) {
  console.log("Detected flutter_inappwebview. Waiting for platformReady...");
  window.addEventListener("flutterInAppWebViewPlatformReady", initializeInAppWebViewBridge);
}else{
  // fallback to default Web SDK initialization
}
Final Version of webengage.js (with WebView detection)
var webengage;
! function (w, e, b, n, g) {
  function o(e, t) {
    e[t[t.length - 1]] = function () {
      r.__queue.push([t.join("."), arguments])
    }
  }
  var i, s, r = w[b],
    z = " ",
    l = "init options track screen onReady".split(z),
    a = "feedback survey notification".split(z),
    c = "options render clear abort".split(z),
    p = "Open Close Submit Complete View Click".split(z),
    u = "identify login logout setAttribute".split(z);
  if (!r || !r.__v) {
    for (w[b] = r = {
      __queue: [],
      __v: "6.0",
      user: {}
    }, i = 0; i < l.length; i++) o(r, [l[i]]);
    for (i = 0; i < a.length; i++) {
      for (r[a[i]] = {}, s = 0; s < c.length; s++) o(r[a[i]], [a[i], c[s]]);
      for (s = 0; s < p.length; s++) o(r[a[i]], [a[i], "on" + p[s]])
    }
    for (i = 0; i < u.length; i++) o(r.user, ["user", u[i]])
  }
}(window, document, "webengage");

if (isInAppWebViewAvailable()) {
  console.log("Detected flutter_inappwebview. Waiting for platformReady...");
  window.addEventListener("flutterInAppWebViewPlatformReady", initializeInAppWebViewBridge);
} else {
  console.log("Flutter WebView not detected. Loading WebEngage Web SDK...");
  setTimeout(function () {
    var f = document.createElement("script"),
      d = document.getElementById("_webengage_script_tag");
    f.type = "text/javascript",
      f.async = !0,
      f.src = ("https:" == window.location.protocol ? "https://ssl.widgets.webengage.com" : "http://cdn.widgets.webengage.com") + "/js/webengage-min-v-6.0.js",
      d.parentNode.insertBefore(f, d)
  });
  webengage.init("YOUR_LICENSE_CODE");
}
Data Center Setup Reminder
🚧

This example uses the Global Data Center (ssl.widgets.webengage.com).
If your account is on a different data center, please update the script URL accordingly.

Refer to official documentation for the correct Data Center URL: WebEngage Web SDK Docs

Step 4 : Flutter Integration Code

Configure your Flutter app to listen for messages from the bridge. This requires webengage_flutter version 1.7.0 or higher

Implementation by Plugin

Choose the plugin you are currently using in your Flutter project for the setup instructions.

webview_flutter :
WebViewWidget(
        controller: WebViewController()
          ..setJavaScriptMode(JavaScriptMode.unrestricted)
          ..loadRequest(Uri.parse(webUrl))
          ..addJavaScriptChannel(WebEngageJSBridge.jsChannelName,
              onMessageReceived: (JavaScriptMessage message) {
            WebEngageJSBridge.handleWebViewFlutterMessage(message.message);
          }));
flutter_inappwebview :
InAppWebView(
        initialUrlRequest: URLRequest(
          url: WebUri(webUrl),
        ),
        onWebViewCreated: (InAppWebViewController controller) async {
          controller.addJavaScriptHandler(
              handlerName: WebEngageJSBridge.jsChannelName,
              callback: (args) {
                WebEngageJSBridge.handleInAppWebViewMessage(args);
              });
        });
📘

WebEngageJSBridge is a Dart helper class used to handle messages from JavaScript and forward them to the WebEngage SDK.
It is available starting from version 1.7.0 of the webengage_flutter package.


User Attributes

List of System User Attributes Defined by WebEngage (Supported from version 2.0.1)

NameTypeDescription
we_first_nameStringUser's first name
we_last_nameStringUser's last name
we_emailStringUser's email address
we_birth_dateStringUser's birth date in yyyy-mm-dd format
we_phoneStringUser's phone number in E.164 format.
Example: +551155256325
we_genderStringUser's gender. Values can only be male, female, or other
we_companyStringUser's company
we_hashed_emailStringEncrypted email address
we_hashed_phoneStringEncrypted phone number
we_push_opt_inBooleanIf set to false, the user will not receive push notifications on any of their devices
we_sms_opt_inBooleanIf set to false, the user will be excluded from promotional SMS campaigns
we_email_opt_inBooleanIf set to false, the user will be excluded from promotional email campaigns

Did this page help you?