tom_d4rt_flutter_ast
Runs D4rt scripts that import `flutter/material.dart` and return real widget trees, on the analyzer-free path. The strategic building block for over-the-air UI in shipping apps.
Overview
From the module readme.md file:
What it enables
Enables over-the-air UI updates, embedded agentic apps, runtime widget rendering.
Relationships
Standalone — no declared relationships.
tom_d4rt_flutter_ast
> Attribution. The tom_d4rt project is an extended clone of the original > d4rt project by Moustapha Kodjo Amadou, initially published in 2025. The > complete interpreter is based on his idea.
D4rt Flutter-Material bridge — execute D4rt scripts that return live Flutter widget trees, without an app-store republish.
Overview
tom_d4rt_flutter_ast connects the D4rt sandboxed Dart interpreter to the full Flutter-Material widget library. A D4rt script can import
package:flutter/material.dart, construct any widget tree it needs, and return it as a real
Widget object that Flutter renders natively inside the host application.
The interpreter runs entirely on the analyzer-free execution path (tom_d4rt_ast
+ tom_d4rt_exec), so it can be embedded in a shipping Flutter app with no Dart analyzer dependency and no platform-channel overhead. That makes this package the right choice for
over-the-air UI updates and web targets: ship widget code in an AstBundle, execute it at runtime, and render the result — no app-store cycle.
> Which package should I use? Start with the source-based > tom_d4rt_flutter
(SourceFlutterD4rt) — it is the > primary, recommended Flutter integration. It parses Dart source directly, has > the simplest workflow, and covers desktop and mobile development. Reach for >
this AST-based package only when you specifically need the analyzer-free > path: embedding in a shipping app for over-the-air updates, or running on the > web (dart2js / dart2wasm). The two packages share the same bridge surface and > script corpus; the AST variant trades the on-device parse step for a > pre-compiled
AstBundle.
Source-based vs analyzer-free — which D4rt family
D4rt ships in two execution families, and tom_d4rt_flutter_ast is the analyzer-free
Flutter member:
- Source-based (analyzer) —
tom_d4rt,tom_d4rt_dcli,
tom_d4rt_flutter. Parses Dart source with the analyzer, giving full type inference and precise error reporting. This is the
stable reference and usually the preferable choice. - Analyzer-free (mirror AST)
— tom_d4rt_ast, tom_ast_model, tom_ast_generator, tom_d4rt_exec,
tom_dcli_exec, tom_d4rt_flutter_ast (this package). Runs from pre-compiled
SAstNode bundles with no analyzer dependency at runtime.
The analyzer-free path is what unlocks the two scenarios the source-based runtime cannot serve:
-
Web — the
analyzerpackage is too large to ship via dart2js / dart2wasm,
so the only way to interpret Dart UI in a browser is from a pre-compiled bundle. - Over-the-air UI updates
— compile a screen to an AstBundle on a server, download it into a shipping app, and render it at runtime — no app-store round trip and no user-side update.
Because generated AST bundles are large, prefer the source-based tom_d4rt_flutter
unless one of those constraints applies.
The bridge layer covers dart:ui plus the following Flutter library barrels:
| Library | Bridge file |
|---|---|
dart:ui | dart_ui_bridges.b.dart |
flutter/painting.dart | painting_bridges.b.dart |
flutter/foundation.dart | foundation_bridges.b.dart |
flutter/animation.dart | animation_bridges.b.dart |
flutter/physics.dart | physics_bridges.b.dart |
flutter/scheduler.dart | scheduler_bridges.b.dart |
flutter/semantics.dart | semantics_bridges.b.dart |
flutter/services.dart | services_bridges.b.dart |
flutter/gestures.dart | gestures_bridges.b.dart |
flutter/rendering.dart | rendering_bridges.b.dart |
flutter/widgets.dart | widgets_bridges.b.dart |
flutter/material.dart | material_widgets_bridges.b.dart |
flutter/cupertino.dart | cupertino_bridges.b.dart |
Installation
Add the package to your Flutter application's pubspec.yaml:
dependencies:
tom_d4rt_flutter_ast: ^0.1.1
Or via the command line:
dart pub add tom_d4rt_flutter_ast
The package pulls in its own transitive D4rt dependencies (tom_d4rt_ast, tom_d4rt_exec,
tom_ast_generator); nothing extra is needed in the consuming project's pubspec.yaml
for those.
Usage
Creating a FlutterD4rt instance
FlutterD4rt is the single entry point. Its default constructor creates a fresh D4rt
interpreter and immediately registers all Flutter-Material bridges:
import 'package:tom_d4rt_flutter_ast/tom_d4rt_flutter_ast.dart';
final d4rt = FlutterD4rt();
If you already have a D4rt instance with other bridges registered (for example, tom_core_d4rt
bridges), wrap it instead of creating a second interpreter:
final base = D4rt();
// … register other bridges on base …
final d4rt = FlutterD4rt.withInterpreter(base);
Building a widget from a D4rt script
A D4rt script that returns a widget must expose a top-level build(BuildContext ctx) function. Compile the source into an
AstBundle, then hand it to FlutterD4rt.build:
// Compile once, reuse the bundle as many times as needed.
final bundle = await d4rt.interpreter.createBundleFromSource('''
import 'package:flutter/material.dart';
dynamic build(BuildContext context) {
return Container(
width: 200.0,
height: 100.0,
color: Colors.blue,
child: const Center(
child: Text('Hello from D4rt!'),
),
);
}
''');
// Synchronous execution — wrap in a Builder to supply the BuildContext.
Widget myWidget = d4rt.build<Widget>(bundle, context);
// Or asynchronously (async entry function, or when called outside a build method).
Widget myWidget = await d4rt.buildAsync<Widget>(bundle, context);
Returning non-widget values
The same API works for any bridged type. The build / buildAsync pair assumes the entry function is named
build; use execute / executeAsync for any other name:
// Return a Color.
final bundle = await d4rt.interpreter.createBundleFromSource('''
import 'package:flutter/painting.dart';
Color main() => Color.fromARGB(255, 100, 150, 200);
''');
final color = await d4rt.executeAsync<Color>(bundle);
// Call a named function with explicit arguments.
final result = await d4rt.executeAsync<Widget>(
bundle,
name: 'render',
namedArgs: {'label': 'Hello'},
);
Resetting between script runs
Call resetScript() between test runs to evict any script-declared top-level names from the interpreter's environment, so successive calls see a clean global scope:
d4rt.resetScript();
Error handling
Any unwrap mismatch (script returned a type that cannot be coerced to T) throws FlutterD4rtException:
try {
final widget = d4rt.build<Widget>(bundle, context);
} on FlutterD4rtException catch (e) {
debugPrint('D4rt execution failed: $e');
}
Features
Full Flutter-Material surface
The bridge covers every class, constructor, named constructor, static method, and property exposed by the thirteen library barrels listed in the overview table. Scripts can use
StatelessWidget, StatefulWidget, State, AnimationController, custom painters, custom scroll physics, Cupertino widgets, and more.
Proxy classes for abstract delegates
Flutter has several abstract delegate types (CustomPainter, MultiChildLayoutDelegate,
SingleChildLayoutDelegate, …) that scripts cannot subclass directly because D4rt cannot instantiate abstract classes. The generated
flutter_proxies.b.dart file provides concrete D4rtCustomPainter, D4rtMultiChildLayoutDelegate, etc. wrappers that accept callback closures from the interpreter and forward calls to native Flutter.
Generic-type relaxers
The generated flutter_relaxers.b.dart file supplies $Relaxed* wrapper classes (e.g.
$RelaxedAbstractLayoutBuilder<V>, $RelaxedTween<T>) that bridge the gap between D4rt's
<dynamic> type arguments and Flutter's concrete generic expectations. These are registered before
FlutterMaterialBridges in FlutterD4rt._registerBridges so that factories resolve in the correct order.
Hand-written D4UserBridge overrides
Three classes in lib/src/d4rt_user_bridges/ override specific auto-generated adapter behaviour where the generator cannot produce a correct implementation:
| User bridge | Target | Why it overrides the generated code |
|---|---|---|
StateUserBridge |
flutter/src/widgets/framework.dart :: State |
Defers
setState
calls that arrive mid-frame via
addPostFrameCallback
, avoiding
Build scheduled during frame
errors.
|
StrutStyleUserBridge |
dart:ui :: StrutStyle |
Always creates
painting.StrutStyle
instead of the opaque engine object so that property access (fontSize, height, fontWeight, …) works inside D4rt scripts.
|
BasicMessageChannelUserBridge |
flutter/src/services/platform_channel.dart :: BasicMessageChannel |
Bypasses the generic-typed
setMessageHandler
signature by installing the handler at the
BinaryMessenger
layer, avoiding a Dart runtime function-type check that the generator cannot satisfy.
|
Each user bridge is a D4UserBridge subclass annotated with @D4rtUserBridge(libraryPath, className). The generator recognises these annotations and routes the relevant method or constructor calls through the override instead of the auto-generated adapter.
Example applications
The companion tom_d4rt_flutter_ast_test project holds 33 self-contained example apps under
tom_d4rt_flutter_ast_test/example/, each a multi-file D4rt program compiled to an AstBundle
and rendered through FlutterD4rt on the analyzer-free path. They are the broadest real-world exercise of the bridge surface and the over-the-air UI scenario:
bezier_curve_editor |
bottom_nav_shell |
bouncing_balls_physics |
calculator |
card_swiper |
carousel_pager |
chat_ui |
clock_face |
color_picker_studio |
conway_life |
counter2 |
counter_app |
drawing_pad |
form_wizard |
kanban_board |
memory_match |
note_app |
particle_field |
photo_gallery_hero |
pomodoro_timer |
slide_puzzle |
snake_game |
solitaire |
solitaire2 |
stopwatch_laps |
stpauls |
stpeters |
sudoku_app |
tabbed_dashboard |
tic_tac_toe |
tip_calculator |
todo_list |
tron |
Run the demo application to browse and execute them interactively:
cd ../tom_d4rt_flutter_ast_test
flutter run # native target
./run_web.sh # dart2js web target
./run_wasm.sh # dart2wasm web target (see script header for status)
Recompile the sample bundles after editing any sample:
flutter test tool/compile_samples_to_bundles.dart
The same sample set is mirrored in the source-direct sibling (tom_d4rt_flutter_test/example/), so the two execution paths can be compared app-for-app.
For a single, repo-curated starter see the d4rt_flutter_sample
in the shared tom_d4rt_samples collection — a focused Flutter-Material script, sitting alongside the interpreter, dcli, and advanced samples. This package's own
example/
folder holds the minimal library-usage snippet.
Documentation
| Doc | What it covers |
|---|---|
| doc/tom_d4rt_flutter_ast_user_guide.md |
Differences-only guide
vs the source-based runtime —
FlutterD4rt
, bundle-driven execution, the sync/async entry points, and the web / over-the-air fit.
|
| doc/creating_fully_dynamic_applications.md |
The over-the-air / fully-dynamic-app playbook — compiling UI to
AstBundle
s on a server, shipping them to a thin host app, and rendering them at runtime.
|
| doc/test_script_context.md |
The script-side contract — the
build
/ named-entry convention, the
BuildContext
argument, and what a bundle-driven script may assume.
|
| doc/tom_d4rt_flutter_ast_limitations.md | AST-specific limitation deltas (no on-device parsing, bundle↔runtime version alignment, web) + backlinks to the base. |
| ../tom_d4rt_flutter/doc/tom_d4rt_flutter_user_guide.md | Base Flutter-runtime guide — shared bridge surface, registration order, performance/GC. Read this first. |
| ../tom_d4rt_flutter/doc/tom_d4rt_flutter_limitations.md | Shared bridge-adapter limits catalogue with script workarounds. |
| ../tom_d4rt_ast/doc/tom_d4rt_ast_user_guide.md | The analyzer-free interpreter core. |
Architecture
Generated bridges vs hand-written user bridges
lib/
src/
bridges/ ← GENERATED — do not edit
dart_ui_bridges.b.dart
painting_bridges.b.dart
foundation_bridges.b.dart
animation_bridges.b.dart
physics_bridges.b.dart
scheduler_bridges.b.dart
semantics_bridges.b.dart
services_bridges.b.dart
gestures_bridges.b.dart
rendering_bridges.b.dart
widgets_bridges.b.dart
material_widgets_bridges.b.dart
cupertino_bridges.b.dart
flutter_bridges_barrel.b.dart ← re-exports all bridge files
flutter_proxies.b.dart ← generated proxy/adapter subclasses
flutter_relaxers.b.dart ← generated generic-type relaxers
material_bridges.b.dart ← FlutterMaterialBridges.register(...)
d4rt_user_bridges/ ← HAND-WRITTEN overrides
basic_message_channel_user_bridge.dart
state_user_bridge.dart
strut_style_user_bridge.dart
d4rt_runtime_registrations.dart ← interface proxies, coercions, factories
flutter_d4rt.dart ← FlutterD4rt + FlutterD4rtException
tom_d4rt_flutter_ast.dart ← public barrel export
tool/
regenerate_bridges.dart ← run to regenerate all *.b.dart files
Every *.b.dart file carries the comment // D4rt Bridge - Generated file, do not edit
at its top. They are produced by tom_d4rt_generator and must never be modified by hand. Manual behaviour corrections belong exclusively in
lib/src/d4rt_user_bridges/ as D4UserBridge subclasses.
Regenerating bridges
Run from the project root whenever the Flutter SDK is upgraded or a bridge definition needs updating:
dart run tool/regenerate_bridges.dart
The tool reads buildkit.yaml in the project root, invokes tom_d4rt_generator.generateBridges(...), and rewrites all
*.b.dart files in lib/src/bridges/. After regeneration, run dart analyze
and the Flutter test suite to verify correctness.
Bridge registration order
FlutterD4rt._registerBridges() registers in a deliberate sequence:
1. registerRelaxers() — generic-type relaxers first so their factories appear below material on the newest-first chain. 2.
registerD4rtRuntimeExtensions() — interface proxies, type coercions, and generic constructor factories (e.g.
GlobalKey, ValueNotifier<int>). 3. FlutterMaterialBridges.register(interpreter)
— all thirteen bridge barrels. 4. interpreter.registerExtensions('tom_d4rt_flutter_ast', registerD4rtInterfaceProxyOverrides)
— post-material proxy overrides that depend on material's registrations being in place. 5. interpreter.finalizeBridges()
— seals the bridge table.
Altering this order will break scripts that use generic-parameterised types.
Ecosystem
The package sits at the top of the D4rt interpreter stack:
tom_ast_model
|
tom_d4rt_ast (analyzer-free AST interpreter runtime)
|
tom_ast_generator (AST bundle compiler)
|
tom_d4rt_exec (execution engine, D4rt, D4rtRunner)
|
tom_d4rt_flutter_ast (THIS — Flutter-Material bridge layer)
tom_d4rt_generator (bridge generator — dev dependency only)
All packages are in the same git repository: github.com/al-the-bear/tom_d4rt, under the
tom_ai/d4rt/ sub-tree.
Related packages
| Package | Relationship |
|---|---|
tom_d4rt_flutter |
Source-based twin — same bridge surface and script corpus on the analyzer path. |
tom_d4rt_ast |
The zero-dependency analyzer-free interpreter core this package runs on. |
tom_ast_generator |
Compiles Dart source into the AstBundles this package executes. |
tom_d4rt_flutter_ast_test
|
The demo/test app and the 33-sample conformance corpus. |
tom_d4rt_generator |
Generates the *.b.dart bridges (dev dependency only). |
Status
- Version:
0.1.1 - Requires Flutter
>=3.27.0, Dart SDK^3.10.4 - D4rt interpreter limits with recommended script-level workarounds live in
this package's doc/tom_d4rt_flutter_ast_limitations.md
delta and the shared base ../tom_d4rt_flutter/doc/tom_d4rt_flutter_limitations.md. - A planned consolidation will move generic D4rt machinery upstream into
tom_d4rt_ast/tom_d4rt_exec, keeping only the Flutter-specific surface in this package.
> The doc/flutter_bugs.md, interpreter_issues.md, > interpreter_unfixable.md,
interpreter_generator_open_issues.md, and > generator_issues.md files are
internal development logs, not user > documentation — they ship for development context only.
Repository: https://github.com/al-the-bear/tom_d4rt/tree/main/tom_d4rt_flutter_ast
License
MIT License Copyright (c) 2025 Moustapha Kodjo Amadou Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Extensions by Peter Nicolai Alexis Kyaw (find me on LinkedIn under Alexis Kyaw). This is a very extended version from the original.