genui_x 0.0.10 copy "genui_x: ^0.0.10" to clipboard
genui_x: ^0.0.10 copied to clipboard

Connect any AI backend — Claude, OpenAI-compatible, or custom proxy — to Google's genui (Generative UI) framework.

genui_x #

pub package pub points likes

A lightweight AI backend adapter for Google's genui (Generative UI) framework.

Connect any AI backend — Anthropic Claude, OpenAI, OpenRouter, LiteLLM, or your own proxy — to genui with a single class.


How genui and genui_x fit together #

Your App
  │
  ├── genui          ← UI engine: renders widgets, manages surfaces
  │     Catalog      ← your widget definitions
  │     Conversation ← drives the chat loop
  │     Surface      ← renders AI-generated UI in your widget tree
  │
  └── genui_x        ← backend wire: HTTP, streaming, auth
        GenuiXTransport ← implements genui's Transport interface

genui handles everything on the UI side — defining widgets, parsing A2UI JSON, and rendering surfaces. It ships no HTTP client.

genui_x provides the missing piece: a Transport that calls any AI backend, streams the response, and feeds it back into genui's rendering pipeline.


Setup #

1. Add dependencies #

# pubspec.yaml
dependencies:
  genui: ^0.8.0
  genui_x: ^0.0.10

2. Create your catalog #

import 'package:genui/genui.dart';

final myCatalog = Catalog(
  [
    CatalogItem(
      name: 'WeatherWidget',
      dataSchema: S.object(
        description: 'Shows weather for a city.',
        properties: {
          'city': S.string(description: 'City name'),
          'temperature': S.number(description: 'Temp in Celsius'),
          'condition': S.string(description: 'e.g. Sunny, Rainy'),
        },
        required: ['city', 'temperature', 'condition'],
      ),
      widgetBuilder: (ctx) {
        final data = ctx.data as Map<String, dynamic>;
        return WeatherWidget(
          city: data['city'] as String,
          temperature: (data['temperature'] as num).toDouble(),
          condition: data['condition'] as String,
        );
      },
    ),
  ],
  catalogId: 'com.example.my_catalog',
);

3. Wire up the conversation #

import 'package:genui/genui.dart';
import 'package:genui_x/genui_x.dart';

final transport = GenuiXTransport(
  apiKey: 'sk-ant-your-key-here',  // Never hardcode in production
  catalog: myCatalog,
  // model: 'claude-sonnet-4-6',   // Optional — default is claude-haiku-4-5
);

final controller = SurfaceController(catalogs: [myCatalog]);

final conversation = Conversation(
  controller: controller,
  transport: transport,
);

// Send a message
await conversation.sendRequest(ChatMessage.user('What is the weather in Tokyo?'));

// Render active surfaces in your widget tree
ValueListenableBuilder(
  valueListenable: conversation.state,
  builder: (context, state, _) {
    return Column(
      children: state.surfaces.map((id) =>
        Surface(surfaceContext: controller.contextFor(id)),
      ).toList(),
    );
  },
);

Backends #

Anthropic Claude (default) #

final transport = GenuiXTransport(
  apiKey: 'sk-ant-your-key',
  catalog: myCatalog,
  model: 'claude-sonnet-4-6', // optional
);

OpenAI #

final transport = GenuiXTransport.openai(
  apiKey: 'sk-your-openai-key',
  catalog: myCatalog,
  // model: 'gpt-4o',  // optional — default is gpt-4o-mini
);

OpenRouter / LiteLLM / custom proxy #

final transport = GenuiXTransport.openai(
  apiKey: 'sk-or-your-key',
  catalog: myCatalog,
  baseUrl: 'https://openrouter.ai/api',
  model: 'anthropic/claude-3.5-sonnet',
);

For a fully custom proxy with non-standard headers:

final transport = GenuiXTransport(
  apiKey: 'your-key',
  catalog: myCatalog,
  baseUrl: 'https://your-proxy.example.com',
  endpointPath: '/v1/chat/completions',
  apiKeyHeader: 'authorization',
  apiKeyPrefix: 'Bearer ',
  streamFormat: GenuiXStreamFormat.openai,
);

Surface operations #

By default the AI can only create new surfaces. Use surfaceOperations to allow updates or deletion:

import 'package:genui/genui.dart'; // for SurfaceOperations

final transport = GenuiXTransport(
  apiKey: 'your-key',
  catalog: myCatalog,
  surfaceOperations: SurfaceOperations.createAndUpdate(dataModel: false),
  // SurfaceOperations.all(dataModel: true)      — create + update + delete + data model
  // SurfaceOperations.updateOnly(dataModel: false) — update only
);

Client data model #

Pass app-state context so the AI knows about the current user or session:

final transport = GenuiXTransport(
  apiKey: 'your-key',
  catalog: myCatalog,
  clientDataModel: {
    'userName': 'Alice',
    'plan': 'pro',
    'locale': 'en-US',
  },
);

Transport controls #

Cancel an in-flight request #

transport.cancel();

Clear conversation history #

transport.clearHistory();

Loading state #

ValueListenableBuilder<bool>(
  valueListenable: transport.isLoading,
  builder: (context, loading, _) {
    return loading ? const CircularProgressIndicator() : const SizedBox.shrink();
  },
);

Debug logging #

final transport = GenuiXTransport(
  apiKey: 'your-key',
  catalog: myCatalog,
  debug: true, // prints request URL, status code, and errors to console
);

Models #

Provider Model Notes
Claude claude-haiku-4-5-20251001 Default — fast, low cost
Claude claude-sonnet-4-6 Balanced quality/cost
Claude claude-opus-4-6 Highest quality
OpenAI gpt-4o-mini Default for .openai()
OpenAI gpt-4o Higher quality
OpenRouter any model slug via GenuiXTransport.openai(baseUrl: ...)

Example #

See the example/ folder for working apps:

# Claude (default)
cd example
flutter run --dart-define=CLAUDE_API_KEY=sk-ant-your-key-here

# Minimal
flutter run -t lib/minimal_main.dart --dart-define=CLAUDE_API_KEY=sk-ant-your-key-here

# OpenAI-compatible proxy
flutter run -t lib/proxy_main.dart \
  --dart-define=PROXY_BASE_URL=https://openrouter.ai/api \
  --dart-define=PROXY_API_KEY=sk-or-your-key \
  --dart-define=PROXY_MODEL=anthropic/claude-3.5-sonnet

API key security #

Never hardcode your API key in client-side code.

  • During development, pass it via --dart-define=API_KEY=...
  • In production, route through your own backend proxy and set baseUrl to your proxy URL

Limitations #

  • Streaming only — non-streaming mode is not supported.
  • Flutter Web — direct API calls to Anthropic or OpenAI will fail due to CORS. Use baseUrl to route through a backend proxy.
  • genui alpha — genui itself is in early development; breaking changes may occur.
1
likes
150
points
203
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Connect any AI backend — Claude, OpenAI-compatible, or custom proxy — to Google's genui (Generative UI) framework.

Repository (GitHub)
View/report issues

Topics

#genui #ai #flutter #llm #openai

License

BSD-3-Clause (license)

Dependencies

flutter, genui, http

More

Packages that depend on genui_x