← D4rt
In the worksrole: extension

tom_d4rt_flutter_ast

tom_d4rt_flutter_ast · v0.1.0

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.

View repository → See License
Status
In the works
LOC
545.5k
Tests
3.0k
Test LOC
3.1M

Overview

`tom_d4rt_flutter_ast` connects the sandboxed interpreter to the full Flutter-Material widget library. A script can import `package:flutter/material.dart`, build any widget tree, and return it as a real `Widget` that Flutter renders natively. It runs entirely on the analyzer-free path (`tom_d4rt_ast` + `tom_d4rt_exec`), so it embeds in a shipping Flutter app with no analyzer dependency. Ship widget code in an `AstBundle`, execute it at runtime, render the result — no app-store cycle. The bridge layer spans `dart:ui` and the Flutter painting, rendering, widgets, material, and cupertino barrels. It is the analyzer-free counterpart to `tom_d4rt_flutter` and the client side of Tom's agentic-app line.

What it enables

Enables over-the-air UI updates, embedded agentic apps, runtime widget rendering.

Relationships

Standalone — no declared relationships.

tom_d4rt_flutter_ast

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. This makes the package the strategic building block for **over-the-air UI updates**: ship widget code in an `AstBundle`, execute it at runtime, and render the result — no app-store cycle.

The bridge layer covers `dart:ui` plus the following Flutter library barrels:

LibraryBridge 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`

Monorepo Setup

`tom_d4rt_flutter_ast` is **not published to pub.dev** (`publish_to: none`). It is consumed within the monorepo via a path dependency.

Add the package to your Flutter application's `pubspec.yaml`:

dependencies:
  tom_d4rt_flutter_ast:
    path: ../tom_d4rt_flutter_ast   # adjust relative path as needed

The package pulls in its own transitive D4rt dependencies (`tom_d4rt_ast`, `tom_d4rt_exec`, `tom_ast_generator`) via their own path entries; 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 bridgeTargetWhy 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.

Documentation

DocWhat it covers
[doc/tom_d4rt_flutter_ast_user_guide.md](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/tom_d4rt_flutter_ast_limitations.md](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](../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](../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](../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](https://github.com/al-the-bear/tom_d4rt), under the `tom_ai/d4rt/` sub-tree.

Status

  • Version: `0.1.0`
  • `publish_to: none` — monorepo-only, not available on pub.dev
  • Requires Flutter `>=3.27.0`, Dart SDK `^3.10.4`
  • Active development: the bug-fix corpus in `doc/flutter_bugs.md` tracks

known Flutter-test-environment issues; 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.

Repository: [https://github.com/al-the-bear/tom_d4rt/tree/main/tom_d4rt_flutter_ast](https://github.com/al-the-bear/tom_d4rt/tree/main/tom_d4rt_flutter_ast)

License
TODO: Add your license here.