An open API service indexing awesome lists of open source software.

https://github.com/vicajilau/genui

Zero-boilerplate code generation for the official flutter/genui package. Use @generativeUI to automatically extract JSON schemas and build CatalogItems at compile-time.
https://github.com/vicajilau/genui

dart flutter generative-ui genkit genui

Last synced: 4 days ago
JSON representation

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

Awesome Lists containing this project

README

          

# 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 (`CatalogItem`s) 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`](./packages/genui_annotations) | Lightweight package containing the `@generativeUI` and `@GenerativeUI` annotations. |
| [`genui_builder`](./packages/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`](./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.

```bash
# Activate Melos globally
dart pub global activate melos
```

### Setup the Workspace

Clone the repository and resolve dependencies:

```bash
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:

```bash
# 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.

```dart
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`.

```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:

```dart
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 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`:

```dart
// 1. Raw Dart Map of JSON Schemas:
Map> 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`):

```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}');
});
}
```

2. **Trigger the export automatically** by appending it to your build command in your `pubspec.yaml` scripts or Melos tasks:

```bash
# 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:

```bash
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](LICENSE) file for details.