TypeScript/JavaScript helper for communicating between a Flutter WebView and the page inside it. It provides a small, typed API for common app actions (fullscreen, status/navigation bar, orientation, storage, device/package info), plus optional plugins like vibration and in‑app browser.
npm install @yandeu/js-bridge
import { WebView } from "@yandeu/js-bridge";
// Optional plugins (import by path):
import { InAppBrowser } from "@yandeu/js-bridge/lib/plugins/inAppBrowser.js";
import { Vibration } from "@yandeu/js-bridge/lib/plugins/vibration.js";
async function init() {
// Detect environment
const platform = await WebView.getPlatform(); // 'android' | 'ios' | 'web' | 'unknown'
if (!WebView.isReady) {
console.log("Not running inside Flutter WebView");
return;
}
// Status bar & navigation bar
await WebView.statusBar.setColor("#000000");
await WebView.statusBar.setIconsBrightness("light");
await WebView.navigationBar.setColor("#000000");
// Orientation
WebView.orientation.setPortrait();
// Persistent storage
await WebView.persistentStorage.setItem("token", "abc123");
const token = await WebView.persistentStorage.getItem("token");
// Plugins
await InAppBrowser.openUrl("https://example.com");
Vibration.vibrate();
}
init();
All core APIs are available on the singleton WebView.
WebView.isWebView: boolean — true if the user agent matches Flutter WebView.WebView.isReady: boolean — set after bridge init when running in Flutter WebView.WebView.getPlatform(): Promise<'android'|'ios'|'web'|'unknown'> — platform detection via Flutter.WebView.fullscreen.enter(): voidWebView.fullscreen.exit(): voidpersistentStorage.setItem(key: string, value: string): Promise<boolean>persistentStorage.getItem(key: string): Promise<string|null>persistentStorage.removeItem(key: string): Promise<boolean>persistentStorage.clear(): Promise<boolean>statusBar.setColor(color: string): Promise<MessageResponse<{}>> — color #RRGGBB or #RRGGBBAA.statusBar.setIconsBrightness(brightness: 'light'|'dark'): Promise<MessageResponse<{}>>navigationBar.setColor(color: string): Promise<MessageResponse<{}>>orientation.setLandscape(): voidorientation.setPortrait(): voidexitApp(): voiddeviceInfo(): Promise<MessageResponse<DeviceInfo>>packageInfo(): Promise<MessageResponse<PackageInfo>>on(event: string, listener: (...args:any[]) => void): void — subscribe to action events sent by Flutter (e.g., a back button action your Flutter side emits).off(event: string, listener: (...args:any[]) => void): voidoffMany(events: string): void — comma‑separated list of event names.The bridge automatically initializes when you first import WebView if running inside a Flutter WebView. However, you can manually control the lifecycle:
import { WebView } from "@yandeu/js-bridge";
// When done with the bridge (e.g., unmounting component, navigating away)
WebView.destroy();
import { WebView, communication } from "@yandeu/js-bridge";
// After calling destroy(), reinitialize if needed
communication.init();
// Now WebView and all plugins are ready to use again
if (WebView.isReady) {
await WebView.statusBar.setColor("#000000");
}
Import plugins from @yandeu/js-bridge/lib/plugins/*.js.
import { Vibration } from "@yandeu/js-bridge/lib/plugins/vibration.js";
// Simple vibration (500ms default)
Vibration.vibrate();
// Custom duration (ms) and amplitude (1-255)
Vibration.vibrate(1000, 255);
// Custom pattern [wait, vibrate, wait, vibrate, ...] and optional intensities (0-255)
await Vibration.vibrateWithPattern([0, 500, 100, 500], [0, 255, 0, 128]);
// Check device capabilities
const hasVibrator = await Vibration.hasVibrator();
const hasAmplitude = await Vibration.hasAmplitudeControl();
const hasCustom = await Vibration.hasCustomVibrationsSupport();
import { InAppBrowser } from "@yandeu/js-bridge/lib/plugins/inAppBrowser.js";
await InAppBrowser.openUrl("https://example.com");
import { Ads } from "@yandeu/js-bridge/lib/plugins/ads.js";
// Set ad unit IDs once
Ads.banner.setAdUnitId("ca-app-pub-XXXX/YYYY");
Ads.interstitial.setAdUnitId("ca-app-pub-XXXX/ZZZZ");
Ads.rewarded.setAdUnitId("ca-app-pub-XXXX/WWWW");
// Banner
Ads.banner.show();
Ads.banner.dispose();
// Interstitial (resolves when the ad is dismissed)
const loaded = await Ads.interstitial.load();
if (loaded) {
const dismissed = await Ads.interstitial.show();
console.log("Ad dismissed:", dismissed);
}
// Rewarded (resolves when the ad is dismissed)
const rewardLoaded = await Ads.rewarded.load();
if (rewardLoaded) {
const result = await Ads.rewarded.show();
if (result.ok) {
console.log("Reward amount:", result.amount);
}
}
// Privacy
if (await Ads.isPrivacyOptionsRequired()) {
Ads.presentPrivacyOptionsForm();
}
All request/response messages conform to these shapes:
export type MessageResponse<T extends Record<string, any>> =
| { ok: true; payload: T }
| { ok: false; errorMessage: string };
MessageResponse<T>. When ok is false, an errorMessage is provided.boolean and log errors internally (e.g., persistent storage helpers).