js_widget_runtime 0.4.98
js_widget_runtime: ^0.4.98 copied to clipboard
Run JavaScript widgets as Flutter widgets using QuickJS (VM) or Web Workers (web).
js_widget_runtime #
A Flutter package that runs JavaScript widgets and renders them as native Flutter UI.
- VM/desktop/mobile: uses
flutter_js(QuickJS / JavaScriptCore). - Web: uses a dedicated
web.Workerspawned from an inline Blob URL. - Opt-in QuickJS FFI backend (VM only): swap
flutter_jsfor the quickjs_runtime package to get fully synchronous host calls — build the native library withbash <quickjs_runtime>/tool/build_quickjs.sh, then passQuickjsWidgetEngineBackendviaJsRuntimeConfig.backend.
The JS side communicates with Flutter through a declarative JSON UI tree and a small async bridge (render, fetchJson, exec, storage, secrets, timers, requestAnimationFrame, etc.).
▶ Live demos — click through the widget gallery in your browser: each demo runs in the real engine (Web Worker backend) on GitHub Pages.
Example widgets #
Real JavaScript, rendered by this package and captured by golden tests — the images below are generated straight from those test runs. Live, interactive versions ship in the example app and run in the browser on the live demos page.
| [yolo-hello] | [calculator] |
| [weather] | [stocks] |
| [crypto] | [animation-showcase] |
| [map] | [showcase: bounce] |
| [showcase: card stack] | [showcase: colors] |
| [3D showcase] | [3D game: dodge] |
| [GLB showcase] | [fitness trainer] |
| [M3 showcase] | [M3 showcase: controls] |
| [M3: bottom sheet] | [M3: dialog] |
| [charts showcase] | [M3: date picker] |
| [pomodoro] | [audio player] |
| [video player] | [webview] |
| [adaptive dashboard] | [3D model viewer] |
yolo-hello (animated gradient + bounce) · calculator (full keyboard logic in JS) · weather (wttr.in fetch) · stocks (live quotes) · crypto (price tickers) · map (OpenStreetMap landmarks) · animation-showcase (menu + per-scene captures from the interactive golden: fade / morph / bounce / cards / drag / pulse / colors) · 3d-showcase (procedural primitives, flutter_cube — shape variants under doc/widgets/3d-showcase-.png) · 3d-game-dodge (dodge-the-blocks game) · 3d-glb-showcase (GLB model with PBR) · fitness-trainer (skeletal-coach workout, flame_3d) · m3-showcase (Material 3 nodes: drawer, appBar, banner, searchBar, segmentedButton, radio, navigationRail, carousel, bottomAppBar, tabBar, fab, popupMenu, navigationBar + overlays: bottomSheet, dialog, snackBar, datePicker, timePicker — extra state frames under doc/widgets/m3-.png) · charts-showcase (fl_chart via the flChart node: line, bar, pie, radar, scatter) · pomodoro (25/5 focus/break cycles, donut ring via flChart pie, completed counter persisted in jsr.storage) · audio-player (playlist transport via the zero-size audio_player node + host JsMediaHost) · video-player (video node, cinematic hero card + SVG source/BoxFit switcher) · 3d-viewer (GLB viewer with control-room UI, SVG control bar) · webview-showcase (webView node, host JsWebViewHost, page-to-widget message bridge) · adaptive-dashboard (adaptive node + gridView.maxCrossAxisExtent + jsr.breakpoint/jsr.adaptive/jsr.onViewport)
Web preview runner #
example/lib/preview.dart is a web-only entry point that renders a single widget full-bleed through the real engine (Web Worker backend) — built for hosting live widget previews behind an iframe:
cd example && flutter build web -t lib/preview.dart --base-href /widgets/preview/
URL contract: ?widget=<id>&theme=dark|light, with widget files fetched over HTTP (?base=<url> to override the source). fetch is enabled only for widgets whose manifest opts into "network": true. CI (.github/workflows/preview-web.yml) publishes the build as the jsr-preview-web artifact.
The runner also powers the live demo site on GitHub Pages —
https://istin.github.io/flutter_js_widget_runtime/ — deployed by
.github/workflows/pages.yml: a landing page (site/index.html, widget
cards built from the fa_widgets catalog) at the root plus the runner under
/preview/ (base-href /flutter_js_widget_runtime/preview/).
Quick start #
flutter pub add js_widget_runtime
import 'package:flutter/material.dart';
import 'package:js_widget_runtime/js_widget_runtime.dart';
class DemoPage extends StatelessWidget {
const DemoPage({super.key});
@override
Widget build(BuildContext context) {
return JsWidgetRuntime(
jsSource: r'''
(function() {
jsr.render({
type: 'center',
child: {
type: 'column',
mainAxisSize: 'min',
children: [
{type: 'text', data: 'Hello from JS!', style: {fontSize: 24}},
{type: 'sizedBox', height: 12},
{
type: 'textButton',
text: 'Tap me',
onTap: 'tap',
},
],
},
});
jsr.onEvent(function(actionId, payload) {
if (actionId === 'tap') {
jsr.setTitle('Tapped!');
}
});
})();
''',
runtimeConfig: JsRuntimeConfig(
onRender: (tree) => print('render: $tree'),
onSetTitle: (title) => print('title: $title'),
onStorageUpdate: (storage) => print('storage: $storage'),
),
);
}
}
See the example/ folder for a runnable menu of sample JS apps.
3D scenes and GLB models #
js_widget_runtime does not bundle a 3D engine. Instead it exposes a Js3dHost
interface so the host app can plug in any engine (Flame 3D, three_dart,
flutter_3d_controller, etc.).
JsRuntimeConfig(
js3dHost: const My3dHost(),
// ... other handlers
);
From JS, create a scene and manipulate it:
jsr.scene3d.create('main', {
camera: { position: [0, 2, 5], target: [0, 0, 0] },
lights: [{ type: 'ambient', color: '#ffffff', intensity: 0.5 }]
});
jsr.render({
type: 'scene3d',
id: 'main',
width: 320,
height: 320
});
jsr.scene3d.addModel('main', {
id: 'player',
src: 'assets/player.glb'
});
jsr.scene3d.playAnimation('main', 'player', 'run');
jsr.scene3d.setTransform('main', 'player', {
position: [1, 0, 0],
rotation: [0, 45, 0],
scale: [1.5, 1.5, 1.5]
});
The example/ app includes a 3d-viewer widget powered by
flutter_3d_controller.
Renderer nodes #
The JSON tree supports these node types:
| Category | Nodes |
|---|---|
| Layout | column, row, stack, center, padding, sizedBox, expanded, flexible, wrap, align, absoluteFill |
| Display | text, icon, markdown, divider, spacer, image, svg, avatar, chip, badge, path, progress indicators |
| Container | container, card, inkWell, safeArea, scroll, blur |
| List | listView, gridView, listTile. Scrolling is configurable: shrinkWrap (default true) and physics ('never' / 'always' / 'platform'; default 'always' for listView, 'never' for gridView). Use shrinkWrap: false in a bounded parent to let the view scroll |
| Input | button, textButton, outlinedButton, iconButton, textField, textArea (multiline input: value, hint, minLines default 3, maxLines default 8, onChange per keystroke, optional onSubmit done action), switch, checkbox, slider, dropdown |
| Animation | animatedContainer, animatedOpacity, animatedPositioned (implicit change animations); entrance — one-shot mount animation (animation: fade/slideUp/slideDown/slideLeft/slideRight/scale/fadeScale, delay ms for staggered lists, duration, curve); animatedSwitcher — view transition when switchKey changes (animation: fade/slideLeft/slideRight/slideUp/scale/fadeScale) |
| Map | map — OpenStreetMap via flutter_map (no API key). Props: center {lat, lng}, zoom, markers [{id, lat, lng, label?, color?}], polylines [{points, color?, width?}], fitBounds; events: onTap ({lat, lng}), onMarkerTap ({id}) |
| Chart | chart — sparkline or bar chart. Props: data (list of numbers; points accepted as alias), chartType (line default, bar), color, fillColor (hex with alpha, e.g. #22c55e33), strokeWidth, height |
| 3D | scene3d — host-provided 3D engine. Props: id, width, height, camera, lights. GLB/GLTF models, cameras and lights are controlled from JS via jsr.scene3d.* |
| Media | video, audio (host-provided via JsMediaHost) |
| Gestures | gestureDetector |
Features #
- Cross-platform JS execution (VM + web).
- Declarative JSON-to-Flutter renderer with layout, input, animation and gesture nodes.
- Effects ported from YoClip: radial gradients, box shadows, blur, 3D transforms, clip, text shadows.
manifest.jsonbased app registry with example widgets.- Injected I/O handlers so the host controls permissions (network, CLI, secure storage).
- Host-specific JS APIs via
JsRuntimeConfig.hostBootstrapJs(e.g.,jsr.yoloit = {...}). - Default in-memory handlers for demo/development use.
License #
MIT