streaming_gen_ui 0.1.0 copy "streaming_gen_ui: ^0.1.0" to clipboard
streaming_gen_ui: ^0.1.0 copied to clipboard

A high-performance streaming Generative UI engine for Flutter. Render interactive components reactively as raw LLM streams flow in character-by-character.

streaming_gen_ui #

The Streaming Generative UI Engine for Flutter #

Render interactive Flutter widgets progressively as raw LLM token streams flow in — character-by-character — without waiting for a complete JSON response.

pub.dev License: MIT

API Docs · GitHub · Widget Catalog


The Problem #

When an LLM outputs a UI as JSON, you're typically forced to:

  1. Wait for the entire JSON payload to finish generating
  2. jsonDecode() it all at once
  3. Build the widget tree in a single frame — everything pops in at once

For long or complex widgets, this means a multi-second blank spinner followed by a jarring layout snap.

The Solution #

streaming_gen_ui parses and renders widgets as tokens arrive. Text streams in like a typewriter. Nested widgets mount and fill in progressively. Buttons start disabled and activate the millisecond their action property resolves. No waiting. No pop-in.

Mix conversational Markdown text and dynamic UI components freely in the same stream — the engine separates and renders them in the correct chronological order automatically.


Quick Start #

dependencies:
  streaming_gen_ui: ^0.1.0
import 'package:streaming_gen_ui/streaming_gen_ui.dart';

// 1. Instantiate with a registry
final genUi = StreamingGenerativeUi(
  registry: Registries.essentials,
);

// 2. Inject the auto-generated prompt fragment into your LLM system prompt
final systemPrompt = genUi.registry.systemPromptFragment;

// 3. Pipe the live token stream in
await genUi.stream(
  llmTokenStream,
  viewId: 'message-42',
  onComplete: (raw) => db.save(raw),
);

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

The LLM wraps UI components in <interface> tags anywhere in its response:

Here's the profile card you asked for:

<interface>{"namespace":"core:card","child":{"namespace":"core:column","children":[
  {"namespace":"core:text","content":"Vincent"},
  {"namespace":"core:elevated_button","child":{"namespace":"core:text","content":"Save"},"action":"save_profile"}
]}}</interface>

Let me know if you'd like any changes!

The text renders as Markdown. The widget renders progressively. Both appear in order.


Features #

Widget-by-Widget Streaming #

Widgets mount immediately and fill in their properties as tokens arrive. Parent containers do not wait for children, and children do not wait for siblings—the entire layout compiles and grows incrementally.

Disabled-to-Active Button Transmutation #

Interactive elements (buttons, inputs) are drawn immediately in a disabled state during streaming to maintain layout stability. The exact millisecond their action properties resolve in the stream, they animate into an active state with a micro-bounce transition.

Self-Documenting Registry #

Every widget in the registry carries its own schema and JSON example. The engine automatically compiles these into a precise LLM system prompt fragment — no manual prompt maintenance, no drift between what the LLM thinks exists and what's actually registered.

// The prompt updates automatically as your registry changes
final systemPrompt = genUi.registry.systemPromptFragment;

Set-Theory Registry Composition #

// Union — combine registries
final registry = Registries.essentials + Registries.dashboard;

// Subtraction — strip what you don't need
final readOnly = Registries.essentials.without(['core:elevated_button', 'core:textfield']);

// Subset — allow only specific widgets
final locked = Registries.essentials.only(['core:text', 'core:card']);

Restore Past Responses #

// Restore a previously saved raw response from your database
genUi.restore(viewId: 'message-42', raw: savedRaw);

The view rebuilds identically from the stored string — no re-prompting the LLM.


Built-In Widget Catalog #

Registry Widgets Use Case
Registries.layout core:column, core:row, core:stack, core:wrap, core:container, core:spacer, core:divider Structural layout primitives
Registries.display core:text, core:heading, core:label, core:badge, core:chip, core:icon, core:avatar, core:code_block Typography and display
Registries.cards core:card, core:stat_card, core:profile_card, core:list_tile, core:key_value_card, core:media_card, core:timeline_item, core:comparison_card General-purpose AI response cards
Registries.status core:alert, core:progress_bar, core:progress_ring, core:skeleton, core:empty_state Feedback and status indicators
Registries.advanced core:terminal_card, core:log_viewer, core:document_card, core:diff_card, core:json_viewer Developer tools, agent output, documents
Registries.dashboard core:metric_tile, core:chart_bar, core:dashboard_header, core:dashboard_grid, core:dashboard_preset Analytics and dashboard layouts

Pre-composed bundles:

Registries.base        // layout + display only — minimal prompt cost
Registries.essentials  // base + cards + status — covers most AI chat apps
Registries.full        // everything

Browse all widgets with live streaming previews at the Widget Catalog →


Custom Widgets #

Registering your own widget takes a single WidgetDefinition:

final myRegistry = WidgetRegistry(
  widgets: {
    "custom:user_card": WidgetDefinition(
      description: "A profile summary card.",
      properties: {
        "name": "String — the user's full name",
        "role": "String — their current title",
      },
      jsonExample: '{"namespace":"custom:user_card","name":"Ada Lovelace","role":"Mathematician"}',
      builder: (context, props) => Card(
        child: Column(
          children: [
            StreamingText(props: props, propertyName: 'name'),
            StreamingText(props: props, propertyName: 'role'),
          ],
        ),
      ),
    ),
  },
);

// Merge seamlessly with built-ins
final genUi = StreamingGenerativeUi(
  registry: Registries.essentials + myRegistry,
);

StreamingText and StreamingWidget are exposed publicly so custom widget builders stay stateless and boilerplate-free.


LLM Provider Setup #

Anthropic #

final tokenStream = anthropic.messages
  .stream(model: 'claude-sonnet-4-20250514', messages: messages)
  .map((event) => event.delta?.text ?? '');

await genUi.stream(tokenStream, viewId: 'view-id');

OpenAI #

final tokenStream = OpenAI.instance.chat
  .createStream(model: 'gpt-4o', messages: messages)
  .map((chunk) => chunk.choices.first.delta.content ?? '');

await genUi.stream(tokenStream, viewId: 'view-id');

Gemini #

final tokenStream = model
  .generateContentStream(content)
  .map((chunk) => chunk.text ?? '');

await genUi.stream(tokenStream, viewId: 'view-id');

API Reference #

StreamingGenerativeUi #

Member Description
StreamingGenerativeUi({required registry, showInternalErrors}) Instantiate the engine
stream(tokenStream, {viewId, onText, onComplete}) Pipe a live token stream
restore({viewId, raw}) Rebuild a past response from a saved string
view(viewId) Get the reactive widget for a view
disposeView(viewId) Free memory for a view

WidgetRegistry #

Member Description
registry + other Union — merge two registries
registry.only(ids) Subset — keep only listed widget IDs
registry.without(ids) Subtraction — remove listed widget IDs
registry.systemPromptFragment Auto-generated LLM system prompt string

Public Leaf Widgets #

Widget Description
StreamingText Binds to a named property on a PropertyStream and renders accumulated text
StreamingWidget Resolves a nested child from a sub-property stream into a widget subtree

Contributing #

Contributions are welcome — especially new widget definitions for the community catalog.

  1. Check open issues before starting
  2. Discuss major changes in an issue first
  3. Run flutter test before submitting a PR
  4. New registry widgets should include a description, properties, and a working jsonExample

License #

MIT — see LICENSE for details.

3
likes
150
points
62
downloads

Documentation

API reference

Publisher

verified publishervincentsanicolas.me

Weekly Downloads

A high-performance streaming Generative UI engine for Flutter. Render interactive components reactively as raw LLM streams flow in character-by-character.

Repository (GitHub)

License

MIT (license)

Dependencies

flutter, llm_json_stream, llm_tag_parser

More

Packages that depend on streaming_gen_ui