ai_ui_render

A Flutter framework for safely generating UI from AI prompts. Render Flutter widgets from JSON with guardrailed component catalogs, streaming support, and rich action handling.

pub package License: MIT

Features

  • Guardrailed Components - AI can only use components you explicitly define in your catalog
  • JSON to Widget Rendering - Convert JSON tree structures into Flutter widgets
  • Streaming Support - Render UI progressively as AI generates it (JSONL format)
  • Visibility Conditions - Show/hide components based on data, auth state, or complex logic
  • Rich Actions - Handle user interactions with confirmation dialogs and callbacks
  • Data Binding - Dynamic values that resolve from your data model
  • Validation - Built-in validators for forms and custom validation support

Screenshots

Components Gallery Data Binding Visibility Conditions Feedback Components

Installation

Add to your pubspec.yaml:

dependencies:
  ai_ui_render: ^0.1.0

Then run:

flutter pub get

Quick Start

1. Define a Component Catalog

The catalog defines which components AI can generate - this is your safety guardrail:

import 'package:ai_ui_render/ai_ui_render.dart';
import 'package:flutter/material.dart';

final catalog = createCatalog('my-app')
  .component(
    'Text',
    builder: (props, children) => Text(
      props['text'] as String? ?? '',
      style: TextStyle(
        fontSize: (props['size'] as num?)?.toDouble() ?? 14,
        fontWeight: props['bold'] == true ? FontWeight.bold : FontWeight.normal,
      ),
    ),
    hasChildren: false,
    description: 'Display text content',
  )
  .component(
    'Button',
    builder: (props, children) => ElevatedButton(
      onPressed: () {
        // Handle action
      },
      child: Text(props['label'] as String? ?? 'Button'),
    ),
    hasChildren: false,
    description: 'Clickable button',
  )
  .component(
    'Card',
    builder: (props, children) => Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(children: children),
      ),
    ),
    description: 'Card container',
  )
  .build();

2. Create a Registry

final registry = createRegistryFromCatalog(catalog);

3. Render a UI Tree

// JSON structure (typically from AI)
final uiTree = UITree.fromJson({
  'root': 'main-card',
  'elements': {
    'main-card': {
      'key': 'main-card',
      'type': 'Card',
      'props': {},
      'children': ['greeting', 'action-btn'],
    },
    'greeting': {
      'key': 'greeting',
      'type': 'Text',
      'props': {'text': 'Hello, World!', 'size': 24, 'bold': true},
      'children': [],
    },
    'action-btn': {
      'key': 'action-btn',
      'type': 'Button',
      'props': {'label': 'Click Me'},
      'children': [],
    },
  },
});

// Render it
StandaloneJsonRenderer(
  tree: uiTree,
  registry: registry,
)

Streaming UI Generation

For AI-powered streaming generation:

final streamController = UIStreamController(
  UIStreamOptions(
    api: 'https://your-api.com/generate',
    onComplete: (tree) => print('Generation complete!'),
    onError: (error) => print('Error: $error'),
  ),
);

// Send a prompt
await streamController.send('Create a dashboard with revenue metrics');

// Use in widget
ListenableBuilder(
  listenable: streamController,
  builder: (context, child) {
    return StandaloneJsonRenderer(
      tree: streamController.tree,
      registry: registry,
      loading: streamController.isStreaming,
    );
  },
)

Visibility Conditions

Control component visibility based on data or auth state:

// In JSON
{
  'key': 'admin-panel',
  'type': 'Card',
  'props': {},
  'visible': {'auth': 'signedIn'},  // Only show when signed in
  'children': [],
}

// Complex conditions
{
  'visible': {
    'and': [
      {'auth': 'signedIn'},
      {'path': '/user/isAdmin'},
      {'gt': [{'path': '/user/level'}, 5]}
    ]
  }
}

Available conditions:

  • true / false - Always visible/hidden
  • {'path': '/data/path'} - Visible when path is truthy
  • {'auth': 'signedIn'} / {'auth': 'signedOut'} - Auth-based
  • {'and': [...]} - All conditions must be true
  • {'or': [...]} - Any condition must be true
  • {'not': {...}} - Negates a condition
  • {'eq': [a, b]}, {'neq': [a, b]} - Equality checks
  • {'gt': [a, b]}, {'gte': [a, b]}, {'lt': [a, b]}, {'lte': [a, b]} - Comparisons

Data Binding

Use dynamic values that resolve from your data model:

// In JSON props
{
  'type': 'Text',
  'props': {
    'text': {'path': '/user/name'}  // Resolves from data model
  }
}

// Provide data when rendering
StandaloneJsonRenderer(
  tree: uiTree,
  registry: registry,
  dataModel: {
    'user': {'name': 'John Doe', 'level': 10},
  },
)

Actions

Define and handle user actions:

final catalog = createCatalog('my-app')
  .action('deleteItem', description: 'Delete an item')
  .action('saveForm', description: 'Save form data')
  .build();

// In JSON
{
  'type': 'Button',
  'props': {
    'label': 'Delete',
    'action': {
      'name': 'deleteItem',
      'params': {'id': {'path': '/selectedItem/id'}},
      'confirm': {
        'title': 'Confirm Delete',
        'message': 'Are you sure you want to delete this item?',
        'variant': 'danger'
      }
    }
  }
}

Validation

Built-in validators for form fields:

final validators = Validators.compose([
  Validators.required('Email is required'),
  Validators.email('Invalid email format'),
]);

final result = validators('[email protected]');
print(result.isValid); // true

Available validators:

  • required - Value must not be null or empty
  • email - Valid email format
  • minLength(n) / maxLength(n) - String length constraints
  • min(n) / max(n) - Numeric constraints
  • pattern(regex) - Regex pattern matching
  • url - Valid URL format

With Providers

For full state management integration:

JsonUIProvider(
  registry: registry,
  initialData: {'user': {'name': 'John'}},
  authState: AuthState(isSignedIn: true),
  actionHandlers: {
    'deleteItem': (params) async {
      // Handle delete
    },
  },
  child: JsonRenderer(
    tree: uiTree,
    registry: registry,
  ),
)

JSON Tree Structure

The UI tree uses a flat structure optimized for LLM generation:

{
  "root": "main-element-key",
  "elements": {
    "main-element-key": {
      "key": "main-element-key",
      "type": "ComponentType",
      "props": { "prop1": "value1" },
      "children": ["child-key-1", "child-key-2"],
      "visible": true
    },
    "child-key-1": {
      "key": "child-key-1",
      "type": "Text",
      "props": { "text": "Hello" },
      "children": []
    }
  }
}

Generate AI Prompts

Generate a prompt describing your catalog for AI:

final prompt = generateCatalogPrompt(catalog);
// Use this prompt to instruct AI about available components

Example App

The example directory contains a comprehensive demo application with 5 interactive pages:

Showcases all 25+ built-in components organized by category:

  • Layout: Card, Row, Column, Container, Center, Expanded, Wrap, Spacer, Divider
  • Text: Text, Heading, RichText
  • Buttons: Button, IconButton
  • Display: Icon, Badge, Avatar, Image, ProgressBar, Chip
  • Input: TextField, Checkbox, Switch
  • Feedback: Alert, Loading

2. Data Binding

Demonstrates dynamic values that resolve from your data model with live updates.

3. Visibility Conditions

Interactive demo of all visibility condition types with toggles to see real-time effects.

4. Actions

Shows the action system with confirmation dialogs, parameters, and success/error handlers.

5. Streaming

Simulates AI streaming UI generation with progressive rendering.

Running the Example

cd example
flutter run

API Reference

Core Classes

Class Description
UITree Flat tree structure containing all UI elements
UIElement Single UI element with type, props, and children
AiUiCatalog Registry of allowed components (safety guardrail)
ComponentRegistry Maps component types to widget builders
DynamicValue<T> Value that can be literal or path reference
VisibilityCondition Condition for showing/hiding elements
Action User action with params, confirm, callbacks

Widgets

Widget Description
JsonRenderer Main renderer with Provider integration
StandaloneJsonRenderer Standalone renderer without Provider
JsonUIProvider Combined provider for data, visibility, actions
UIStreamProvider Provider for streaming UI generation

Functions

Function Description
createCatalog(name) Create a catalog builder
createRegistryFromCatalog(catalog) Create registry from catalog
generateCatalogPrompt(catalog) Generate AI prompt from catalog
evaluateVisibility(condition, ctx) Evaluate visibility condition
resolveAction(action, dataModel) Resolve dynamic values in action
validateForm(data, validations) Validate form data

Validators

Validators.required([message])      // Not null or empty
Validators.email([message])         // Valid email format
Validators.minLength(n, [message])  // Min string length
Validators.maxLength(n, [message])  // Max string length
Validators.min(n, [message])        // Min numeric value
Validators.max(n, [message])        // Max numeric value
Validators.pattern(regex, [message]) // Regex pattern
Validators.url([message])           // Valid URL
Validators.compose([...])           // Combine validators

Architecture

┌─────────────────────────────────────────────────────────┐
│                      Your App                           │
├─────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐  │
│  │   Catalog   │  │  Registry   │  │  Data Model     │  │
│  │ (Components)│  │ (Builders)  │  │  (State)        │  │
│  └──────┬──────┘  └──────┬──────┘  └────────┬────────┘  │
│         │                │                   │          │
│         ▼                ▼                   ▼          │
│  ┌─────────────────────────────────────────────────────┐│
│  │              JsonRenderer / StandaloneJsonRenderer  ││
│  │  ┌─────────────────────────────────────────────┐   ││
│  │  │              ElementBuilder                  │   ││
│  │  │  - Resolves dynamic values                   │   ││
│  │  │  - Evaluates visibility                      │   ││
│  │  │  - Builds widgets recursively                │   ││
│  │  └─────────────────────────────────────────────┘   ││
│  └─────────────────────────────────────────────────────┘│
│                          │                              │
│                          ▼                              │
│  ┌─────────────────────────────────────────────────────┐│
│  │                  Flutter Widgets                    ││
│  └─────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────┘

         ┌──────────────────────────────────┐
         │         AI / LLM                 │
         │  ┌────────────────────────────┐  │
         │  │  Catalog Prompt            │  │
         │  │  (Generated from catalog)  │  │
         │  └────────────────────────────┘  │
         │              │                   │
         │              ▼                   │
         │  ┌────────────────────────────┐  │
         │  │  JSON UI Tree              │  │
         │  │  (Streamed via JSONL)      │  │
         │  └────────────────────────────┘  │
         └──────────────────────────────────┘

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License - see LICENSE for details.

Libraries

ai_ui_render
AI UI Render - Flutter JSON to UI rendering framework