FLUTTER & DART UTILITY

Real-Time Generative User Interface Rendering & Processing Engine

A highly modular, event-driven generative UI layout engine for Flutter applications. Designed to decouple AI transport layers from front-end widget trees, it handles namespaced token streams, compiles partial JSON component schemas via reactive builders, manages multi-view target-routing tags, and dynamically activates passive inputs as streams finalize.

The Concept

Building generative AI interfaces usually involves tightly coupling LLM prompts, stream parsing logic, state management, and widget rendering. We wanted to decouple this entirely. The StreamingGenUi controller provides a clean boundary: we hand the developer the system prompt fragment, they manage their own LLM calls, and then they simply pipe the raw response stream back into the controller.

DIAGRAM / IMAGE PLACEHOLDER

Schematic of the decoupled architecture: Developer provides the stream, Controller targets the View ID.

Syntax and Usage

The API is designed for minimal boilerplate and maximum flexibility. Views can be dynamically requested and populated with AI-generated widget trees across your entire application.

The standard layout for streaming and rendering. Set up the controller, pipe the stream in, save raw data on completion, and place the view anywhere in your widget tree.

INTENDED MINIMAL USAGE
// Setup
final genUi = StreamingGenUi(registry: myRegistry);

// Inject the prompt fragment into your LLM system prompt however you want
final systemPrompt = genUi.systemPrompt;

// Pipe the LLM response stream in, save raw response on completion
await genUi.stream(
  llmStream,
  viewId: 'message-42',
  onComplete: (raw) => db.save(raw),
);

// Restore a past response from DB
genUi.restore(viewId: 'message-42', raw: db.load('message-42'));

// Place the view anywhere in your widget tree
genUi.view('message-42')

// Cleanup when permanently gone
genUi.disposeView('message-42');

The complete API layout showcasing custom text callbacks, multi-view target tag-routing, text streaming hooks, and component lifecycle management.

FULL API REFERENCE
// Setup
final genUi = StreamingGenUi(registry: myRegistry);

// System prompt fragment to inject into your LLM
final systemPrompt = genUi.systemPrompt;

// Stream — all parameters
await genUi.stream(
  llmStream,
  viewId: 'side-panel',          // optional — omit if only using callbacks
  onText: (chunk) => ...,        // text portions only, tags stripped, as they stream
  onComplete: (raw) => ...,      // full raw response (text + tags) for DB storage
);

// Multiple views from one response via <interface viewId="..."> tags
// The LLM can target any mounted view by its ID in the tag itself
await genUi.stream(
  llmStream,
  viewId: 'message-42',          // default target for untagged <interface> blocks
  onText: (chunk) => ...,
  onComplete: (raw) => db.save(raw),
);

// Restore from saved raw response
genUi.restore(viewId: 'message-42', raw: savedRawString);

// Place views anywhere in the widget tree
genUi.view('side-panel')         // Flowserract side panel case
genUi.view('message-42')         // Chat bubble case  
genUi.view('global-modal')       // Global action case

// Cleanup
genUi.disposeView('message-42');

Separation of Concerns

This utility establishes clear responsibilities to ensure the developer maintains full control over the application's networking layer and conversational state.

DEVELOPER HANDLES

The Transport Layer

You handle registering the initial UI components, maintaining the chat history state, calling the actual LLM API, and rendering the fallback text states.

CONTROLLER HANDLES

The Parsing & Piping

We provide the system prompt instructions, parse the AI's complex stream responses, and automatically pipe the successfully parsed generative UI directly to the requested view ID.

Basically: We give them a prompt fragment, they share the response and which view we pipe the UI to, and we display the UI of a view ID requested.

Streaming View & Target Routing

When the LLM streams its response, the model can seamlessly embed a dynamic UI widget tree by enclosing a valid JSON payload within custom <interface> and </interface> tags. The engine isolates these tagged blocks to render the visual UI components on-the-fly, supporting two distinct routing modes:

MODE 01

Single-View / Inline Chat Mode

If the model streams an untagged <interface> block, all components are rendered inline. The default .view('message-42') widget handles the entire sandwich layout, automatically converting standard text portions to Markdown and wrapping the dynamic widget tree smoothly.

MODE 02

Multi-View / Target-Routed Mode

If the model streams an interface tag specifying a view (e.g., <interface viewId="side-panel">), the default view only displays text while the target view container (mounted elsewhere via genUi.view('side-panel')) dynamically catches and builds the layout.

* Graceful fallback: If an unknown widget is encountered, developers can supply a custom onUnknownWidget handler, which defaults to displaying a descriptive error layout.

JSON Protocol & ID Registry

Rather than using complex nested schemas that are highly prone to hallucination, the engine uses flat maps for each widget, allowing nesting to represent clean hierarchy structure.

PROTOCOL WIDGET JSON
// Nested column and text widget layout
{
  "namespace": "core:column",
  "children": [
    {
      "namespace": "core:text",
      "text": "Do not press the button!"
    },
    {
      "namespace": "core:elevated_button",
      "child": {
        "namespace": "core:text",
        "text": "The button."
      }
    }
  ]
}

To resolve prompts overlapping and duplicate entries, built-in and custom widgets are registered with a strict namespacing ID system resembling a Minecraft-like format: <provider>:name_in_snake_case

All built-in system widgets use the core provider. The Alpha V1 Built-In Widget Registry contains: Text, Button, Column, Row, Container, and Textfield.

Streaming Generative Architecture

Powered by llm_json_stream, the system rejects static decoding, building widgets progressively token-by-token. This updates the .register() signature to feed property streams:

STREAM-AWARE BUILDER SIGNATURE
void register(String namespace, Widget Function(MapPropertyStream mapStream) builder)
TECH 01

Hierarchical Prop Passing

Sub-streams are extracted (MapPropertyStream / ListPropertyStream) and passed recursively. Parents immediately mount child layouts, allowing child nodes to update self-reactively on their own streams without blocking layout.

TECH 02

Accumulating String Builder

To eliminate jarring UI jumps or frame drops during parsing, text elements use an accumulating string builder that grows progressively on every raw text token chunk, maintaining perfect conversational context.

TECH 03

Dynamic Action Activation

Form buttons paint greyed out and disabled. The moment the LLM streams the action payload, the callback resolves the future (mapStream.getStringProperty('action').future), activating and transitioning the button dynamically.

TECH 04

Centralized Interactions

Interactions (like form submissions or button clicks) are mapped back to the app layer using a centralized action dispatcher. Developers register active callbacks on the controller to receive real-time updates.