Skip to content

vicajilau/genui

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

72 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

GenUI: Code Generation for Flutter Generative UI

GenUI Logo

CI Status Web CD Status

The developer experience (DX) companion for the official Google Generative UI (GenUI) ecosystem in Flutter.

GenUI code generation automatically extracts data schemas and generates type-safe builders for your widgets at compile time. Instead of manually writing JSON schemas and writing repetitive catalog adapters, you can focus on building standard Flutter widgets.


πŸš€ Key Features

  • Zero-Boilerplate Catalogs: Simply decorate your Flutter widget with @generativeUI. The build system automatically handles schema mapping and widget instantiation.
  • Compile-Time Introspection: Uses Dart's AST (Abstract Syntax Tree) and build_runner to extract property constraints, avoiding runtime reflection overhead.
  • Two-Phase Code Generation: Automatically generates highly optimized local component schemas (CatalogItems) AND assembles a central genui_registry.g.dart catalog index. No manual wiring required.
  • Automatic Event Mapping: Event callback properties (like VoidCallback) are automatically mapped to dispatch A2UI events (dispatchEvent(UserActionEvent)), sending widget states back to the LLM.
  • Seamless Integration: Built directly on top of the official package:genui and package:json_schema_builder.

✨ The Developer Experience (DX)

  1. Zero Boilerplate: Decorate your Flutter widget with @generativeUI.
  2. Auto-Discovery: You don't need to manually register your widgets into a massive list. The genui_builder crawls your project and creates a single globalGenUICatalog containing everything.
  3. Fully Type-Safe: Automatically casts itemContext.data values to your widget constructor fields, preventing type mismatches at runtime.

πŸ“ Workspace Structure

This monorepo utilizes Dart Pub Workspaces and Melos to ensure clean dependency graphs.

Package Description
genui_annotations Lightweight package containing the @generativeUI and @GenerativeUI annotations.
genui_builder The AST code generator using build_runner and analyzer to extract component schemas and build the global catalog index at compile time.
example The playground Flutter app demonstrating the integration with Google's official package:genui Surface and SurfaceController.

πŸ› οΈ Getting Started

Prerequisites

Ensure you have Dart SDK ^3.12.0 or Flutter SDK installed. You also need Melos activated globally.

# Activate Melos globally
dart pub global activate melos

Setup the Workspace

Clone the repository and resolve dependencies:

git clone https://github.com/vicajilau/genui.git
cd genui

# Link dependencies natively across the workspace
flutter pub get

Code Generation

To generate your UI schemas and the global registry, run:

# Triggers code generation across all workspace packages
melos run build_runner

πŸ“– Usage Example

1. Annotate your Widgets

Import the annotations package, decorate your widget, and include the .genui.g.dart part directive.

import 'package:flutter/material.dart';
import 'package:genui/genui.dart';
import 'package:genui_annotations/genui_annotations.dart';
import 'package:json_schema_builder/json_schema_builder.dart';

part 'user_card.genui.g.dart';

@generativeUI
class UserCard extends StatelessWidget {
  final String username;
  final String role;

  const UserCard({
    super.key,
    required this.username,
    required this.role,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      child: ListTile(
        title: Text(username),
        subtitle: Text(role),
      ),
    );
  }
}

2. Consume the Global Catalog

Run the code generator. GenUI will automatically compile your widget into a CatalogItem and export it to a global Catalog in lib/genui_registry.g.dart.

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

// Auto-generated by GenUI Builder
import 'genui_registry.g.dart';

void main() {
  // Inject the global catalog into the official SurfaceController
  // Note: globalGenUICatalog automatically includes Google's official layout elements (Row, Column, Text, etc.) alongside your custom widgets!
  final controller = SurfaceController(catalogs: [globalGenUICatalog]);

  runApp(MainApp(controller: controller));
}

class MainApp extends StatelessWidget {
  final SurfaceController controller;
  const MainApp({super.key, required this.controller});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          // Render dynamic surfaces automatically using your catalog!
          child: Surface(
            surfaceContext: controller.contextFor('demo_surface'),
          ),
        ),
      ),
    );
  }
}

3. Handle Events Type-Safely

If your widget defines interactive callbacks (like VoidCallback onToggle), you can parse and process them cleanly using GenUiEvent and auto-generated event constants:

import 'package:genui_annotations/genui_annotations.dart';
// Import the generated events:
import 'widgets/user_card.genui.g.dart';

void handleWidgetEvent(String eventJson) {
  final event = GenUiEvent.parse(eventJson);
  if (event == null) return;

  if (event.name == UserCardEvents.onToggle) {
    print('UserCard clicked for component ID: ${event.sourceComponentId}');
    print('Widget properties context: ${event.context}');
  }
}

Note: For callbacks that take arguments (e.g. ValueChanged<bool> onChanged), parameter names and values are automatically appended to the event's context map (e.g. event.context['value']).

4. Decoupled AI Schema Sharing (e.g. Google Genkit)

GenUI supports exporting your UI component schemas in a framework-agnostic way. This is ideal if you are orchestrating your AI models in a separate Node.js, Go, or Python backend (like Google Genkit).

In-Memory Usage (Client-Side LLM)

If your AI runs locally on the device or if you have a Dart backend, you can fetch pre-formatted schemas directly from genui_registry.g.dart:

// 1. Raw Dart Map of JSON Schemas:
Map<String, Map<String, dynamic>> schemas = globalGenUISchemas;

// 2. Pre-formatted Markdown list ready for LLM System Instructions:
String systemInstruction = '''
You are a GenUI assistant. You must respond using components defined here:
$globalGenUISchemasPromptDescription
''';

Static File Export (Backend Genkit/TypeScript)

To use your schemas in a separate backend repository (e.g., Node.js with Genkit), you can export the schemas to a static JSON file.

Because the generated registry transitively imports package:flutter (which has native graphic/engine bindings), running a standalone Dart script via dart run in the console will fail. Instead, we run a headless script using the flutter test runner, which resolves all Flutter engine bindings correctly.

To set this up in your own Flutter project:

  1. Create the export script as a test file (e.g., test/genui_schemas_export_test.dart):
import 'dart:convert';
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:your_app_name/genui_registry.g.dart'; // Import your generated registry

void main() {
  test('Export GenUI schemas to JSON', () {
    final schemas = globalGenUISchemas;
    final jsonString = const JsonEncoder.withIndent('  ').convert(schemas);
    
    // Auto-detect the project root and write to build/genui_schemas.json
    final envPath = Platform.environment['GENUI_EXPORT_PATH'];
    final file = envPath != null && envPath.isNotEmpty
        ? File(envPath)
        : File('build/genui_schemas.json');
        
    file.parent.createSync(recursive: true);
    file.writeAsStringSync(jsonString);
    print('βœ“ Schemas exported to: ${file.absolute.path}');
  });
}
  1. Trigger the export automatically by appending it to your build command in your pubspec.yaml scripts or Melos tasks:
# Run build_runner, then run the test script to write the JSON schema contract
dart run build_runner build && flutter test test/genui_schemas_export_test.dart

By default, this will write the schema to build/genui_schemas.json relative to your project's root.

If you want to copy the file directly to a custom directory (e.g., to a sibling folder containing your Node.js Genkit backend), you can use the GENUI_EXPORT_PATH environment variable:

GENUI_EXPORT_PATH=../my-genkit-backend/src/genui_schemas.json flutter test test/genui_schemas_export_test.dart

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Zero-boilerplate code generation for the official flutter/genui package. Use @GenerativeUI to automatically extract JSON schemas and build CatalogItems at compile-time.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Contributors