Adding event listeners
Our widgets will publish events to the following interfaces if they are available:
window.Android.postMessage
window.webkit.messageHandlers.ios.postMessage
window.ReactNativeWebView.postMessage
Events are sent as JSON with the following format:
{
name,
type,
metadata
}
We also dispatch these events to the top-level window with window.dispatchEvent
( with the above payload in the event.details)
Code examples
See the below code examples of listening for widget events.
Webview
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.JavascriptInterface
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.fragment.app.Fragment
import com.example.basicnav.databinding.FragmentDashboardBinding
import org.json.JSONObject
const val WIDGET_URI = "https://widget-domain-that-we-provide"
class DashboardFragment : Fragment() {
private var _binding: FragmentDashboardBinding? = null
private val binding get() = _binding!!
private lateinit var webView: WebView
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDashboardBinding.inflate(inflater, container, false)
val root: View = binding.root
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true)
}
webView = binding.webView
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
val jsCode = """
if (window.__boot && typeof window.__boot === 'function') {
window.__boot('$token', { numCreditCards: $numCreditCards, hash: '$hash', widget_id: '$widgetId' });
}
"""
webView.evaluateJavascript(jsCode, null)
}
}
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.addJavascriptInterface(WebAppInterface(this), "Android")
return root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Retrieve the fragment from the arguments
val fragment = arguments?.getString("fragment")
val widgetId = "your-widget-id"
val token = "token-for-user"
Log.d("DashboardFragment", "URL fragment received: $fragment")
// Load the WebView
webView.loadUrl(WIDGET_URI)
}
fun handleMessageFromWebView(message: String) {
Log.d("DashboardFragment", "Received message from WebView: $message")
try {
val jsonObject = JSONObject(message)
when (val type = jsonObject.getString("type")) {
"externalLink" -> {
val url = jsonObject.getString("url")
openExternalLink(url)
}
else -> {
Log.d("DashboardFragment", "Received message $type")
}
}
} catch (e: Exception) {
Log.e("DashboardFragment", "Error parsing JSON message", e)
}
}
private fun openExternalLink(url: String) {
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
} catch (e: Exception) {
Log.e("DashboardFragment", "Error opening external link", e)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
// WebAppInterface.kt
class WebAppInterface(private val fragment: DashboardFragment) {
@JavascriptInterface
fun postMessage(message: String) {
// Handle the received message
fragment.activity?.runOnUiThread {
fragment.handleMessageFromWebView(message)
}
}
}
import UIKit
import WebKit
// Define a struct for the payload
struct Payload: Codable {
let type: String
let url: String
}
class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Set up the web view configuration
let preferences = WKPreferences()
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
configuration.userContentController.add(self, name: "ios")
// Initialize the web view
webView = WKWebView(frame: self.view.frame, configuration: configuration)
webView.navigationDelegate = self
self.view.addSubview(webView)
// Define the widget URL and parameters
let widgetUrl = "https://widget-url"
let token = "your-token"
let numCreditCards = 2 // Example of additional config
let hash = "your-hash" // Received from the universal link
let widgetId = "your-widget-id"
// Load the URL
if let url = URL(string: widgetUrl) {
webView.load(URLRequest(url: url))
}
}
// WKNavigationDelegate method
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// JavaScript code to be injected
let jsCode = """
if (window.__boot && typeof window.__boot === 'function') {
window.__boot('\(token)', { numCreditCards: \(numCreditCards), hash: '\(hash)', widget_id: '\(widgetId)' });
}
"""
webView.evaluateJavaScript(jsCode, completionHandler: nil)
}
// WKScriptMessageHandler method
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "ios", let jsonString = message.body as? String {
print("Received message from JS: \(jsonString)")
guard let jsonData = jsonString.data(using: .utf8) else {
fatalError("Failed to convert string to data")
}
do {
let decoder = JSONDecoder()
let payload = try decoder.decode(Payload.self, from: jsonData)
if payload.type == "externalLink", let url = URL(string: payload.url) {
print("OPEN URL: \(payload.url)")
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else {
print("Cannot open URL")
}
}
} catch {
print("Error decoding JSON:", error)
}
}
}
}
import React, { useRef } from 'react';
import { WebView } from 'react-native-webview';
const WIDGET_URI = 'https://widget-domain-that-we-provide';
const DashboardScreen = () => {
const webViewRef = useRef(null);
const handleMessage = (event) => {
try {
const message = JSON.parse(event.nativeEvent.data);
console.log('Received message from WebView:', message);
if (message.type === 'externalLink') {
const url = message.url;
openExternalLink(url);
} else {
console.log('Received message type:', message.type);
}
} catch (error) {
console.error('Error parsing JSON message:', error);
}
};
const openExternalLink = (url) => {
// handle opening of external link here
};
const onLoadEnd = () => {
const token = 'your-token';
const numCreditCards = 2;
const hash = 'your-hash';
const widgetId = 'your-widget-id';
const jsCode = `
if (window.__boot && typeof window.__boot === 'function') {
window.__boot('${token}', { numCreditCards: ${numCreditCards}, hash: '${hash}', widget_id: '${widgetId}' });
}
`;
webViewRef.current.injectJavaScript(jsCode);
};
return (
<div>
<WebView
ref={webViewRef}
source={{ uri: WIDGET_URI }}
onMessage={handleMessage}
onLoadEnd={onLoadEnd}
javaScriptEnabled={true}
domStorageEnabled={true}
/>
</div>
);
};
export default DashboardScreen;
// main.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(
MaterialApp(theme: ThemeData(useMaterial3: true), home: const WebViewApp()),
);
}
class WebViewApp extends StatefulWidget {
const WebViewApp({super.key});
@override
State<WebViewApp> createState() => _WebViewAppState();
}
class _WebViewAppState extends State<WebViewApp> {
late final WebViewController controller;
bool _hasBooted = false;
Future<String?> fetchToken() async {
try {
final response = await http.get(
Uri.parse('your_token_url'),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
return data['access_token'];
} else {
debugPrint('Failed to fetch token: ${response.statusCode}');
}
} catch (e) {
debugPrint('Error fetching token: $e');
}
return null;
}
@override
void initState() {
super.initState();
controller =
WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..loadRequest(
Uri.parse('your_widget_url'),
)
..addJavaScriptChannel(
'Android',
onMessageReceived: handleMessageFromWebView,
)
..addJavaScriptChannel(
'webkit.messageHandlers.iosListener',
onMessageReceived: handleMessageFromWebView,
)
..setNavigationDelegate(
NavigationDelegate(
onPageFinished: (String url) async {
if (!_hasBooted) {
final token = await fetchToken();
if (token != null) {
await controller.runJavaScript(
'window.__boot("$token", {widget_id: "your_widget_id"})',
);
_hasBooted = true;
}
}
},
),
);
}
void handleMessageFromWebView(JavaScriptMessage message) {
debugPrint('\x1B[33m[WebView] ${message.message}\x1B[0m'); // Yellow color
try {
final jsonObject = jsonDecode(message.message);
final type = jsonObject['type'] as String;
switch (type) {
case 'externalLink':
final url = jsonObject['url'] as String;
openExternalLink(url);
default:
debugPrint('\x1B[36m[WebView Event] $type\x1B[0m'); // Cyan color
}
} catch (e) {
debugPrint('\x1B[31m[WebView Error] $e\x1B[0m'); // Red color
}
}
void openExternalLink(String url) async {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri);
} else {
debugPrint('Could not launch $url');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Flutter WebView')),
body: WebViewWidget(controller: controller),
);
}
}
iFrame
import React, { useEffect, useRef, useState } from 'react';
import axios from 'axios';
function App() {
const iframeRef = useRef(null);
const [hasBooted, setHasBooted] = useState(false);
const fetchToken = async () => {
// get your token here
};
const bootWidget = async () => {
const token = await fetchToken();
if (iframeRef.current && token) {
const config = {
action: 'boot',
token: token,
params: {
widget_id: 'your_widget_id',
// Add any additional options here
},
};
// Send the message to the iframe
iframeRef.current.contentWindow.postMessage(config, '*');
setHasBooted(true);
}
};
useEffect(() => {
// Define the event listener function
const handleMessage = async (event) => {
if (event.data?.name !== "react-devtools-content-script") {
console.log('Widget Event:', event.data);
}
// Handle external link event
if (event.data?.type === 'externalLink' && event.data?.url) {
window.open(event.data.url, '_blank');
}
};
// Add the event listener
window.addEventListener('message', handleMessage);
// Clean up the event listener on component unmount
return () => {
window.removeEventListener('message', handleMessage);
};
}, []);
return (
<div style={{ textAlign: 'center', marginTop: '20px' }}>
<div id="widget-container">
<iframe
id="widget-frame"
ref={iframeRef}
src="your_widget_url"
title="Widget Frame"
/>
</div>
</div>
);
}
export default App;
Updated 2 days ago
What’s Next