A starter Flutter app for building Generative UI (GenUI) experiences. Instead of the model replying with plain text, it replies with a user interface: buttons, lists, cards, forms, and more, rendered live as real Flutter widgets.
This template wires up a model hosted on Featherless.ai to Flutter's genui package so you can start shaping that experience right away. You bring two things: a catalog of widgets the model is allowed to use, and a system prompt that tells it how to behave. The template handles everything in between.
New to GenUI? That's fine. This README walks you through it from scratch, including installing Flutter.
A normal chat app sends your message to a model and gets text back. GenUI sends your message to a model and gets back a structured description of a UI (in a format called A2UI, "agent-to-UI"). The genui package turns that description into live Flutter widgets on screen.
The model can only ever describe widgets you've told it about. That list of allowed widgets is the catalog. Because the same catalog is fed to the model and used to render, the model can never ask for something your app can't draw.
So the two knobs you'll touch most are:
lib/catalog.dart— what the model can build (the widget vocabulary).lib/prompt.dart— how the model should behave (persona, tone, rules).
Everything else in this template is plumbing that connects those two things to Featherless and to the screen.
This section assumes you have never installed Flutter. We'll run the app as a native desktop app, which is the quickest path: no simulators or devices needed. Follow the instructions for your operating system below.
macOS
- Install Xcode from the App Store (required to build macOS apps). After it installs, open it once so it can finish setting up, then run:
sudo xcodebuild -runFirstLaunch
- Install Flutter. If you have Homebrew:
Otherwise, follow the manual steps at docs.flutter.dev/get-started/install/macos.
brew install --cask flutter
- Confirm everything is healthy. This checks your toolchain and tells you if anything is missing:
You want green checkmarks for Flutter and Xcode at minimum. Don't worry if Android/Chrome show warnings; you don't need them for macOS.
flutter doctor
Windows
- Install Visual Studio (the IDE, not VS Code) with the "Desktop development with C++" workload. This is required to build Windows desktop apps.
- Install Flutter. If you have winget (built into Windows 10/11), open PowerShell and run:
Otherwise, follow the manual steps at docs.flutter.dev/get-started/install/windows. After installing, close and reopen your terminal so
winget install --id=Google.Flutter -e
flutteris on yourPATH. - Confirm everything is healthy. This checks your toolchain and tells you if anything is missing:
You want green checkmarks for Flutter and Visual Studio at minimum. Don't worry if Android/Chrome show warnings; you don't need them for Windows desktop.
flutter doctor
Linux
- Install the build dependencies for Linux desktop apps. On Debian/Ubuntu:
(On Fedora/Arch the package names differ; see the Flutter docs linked below.)
sudo apt-get update sudo apt-get install -y curl git unzip xz-utils zip libglu1-mesa \ clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev
- Install Flutter. The simplest cross-distro option is snap:
Otherwise, follow the manual steps at docs.flutter.dev/get-started/install/linux.
sudo snap install flutter --classic
- Confirm everything is healthy. This checks your toolchain and tells you if anything is missing:
You want green checkmarks for Flutter and Linux toolchain at minimum. Don't worry if Android/Chrome show warnings; you don't need them for Linux desktop.
flutter doctor
This project targets the Flutter SDK that ships Dart ^3.12.1 (see pubspec.yaml). If flutter doctor reports an older Dart, run flutter upgrade.
The app talks to a model hosted on Featherless, which needs an API key.
- Go to featherless.ai and sign in.
- Open your account settings and create an API key.
- Copy the key somewhere safe. You'll paste it in the next step.
The key is not stored in the project. You pass it in at run time, so it never ends up in source control.
From the project root:
flutter pub getEnable desktop support for your platform once (harmless if already enabled):
# macOS
flutter config --enable-macos-desktop
# Windows
flutter config --enable-windows-desktop
# Linux
flutter config --enable-linux-desktopThen run, passing your Featherless key in via --dart-define. Use the device matching your OS:
# macOS
flutter run -d macos --dart-define=FEATHERLESS_API_KEY=your_key_here
# Windows
flutter run -d windows --dart-define=FEATHERLESS_API_KEY=your_key_here
# Linux
flutter run -d linux --dart-define=FEATHERLESS_API_KEY=your_key_hereReplace your_key_here with the key from step 2. The first build takes a minute or two; later runs are faster.
Windows note: In PowerShell the command above works as-is. If your key contains special characters, wrap the whole
--dart-definevalue in quotes:"--dart-define=FEATHERLESS_API_KEY=your_key_here".
Why
--dart-define? It injects the key as a compile-time constant the app reads viaString.fromEnvironment('FEATHERLESS_API_KEY')(see lib/model/featherless_model_client.dart). This keeps your secret out of the codebase. If you forget the flag or the key is invalid, the app shows a SnackBar with the error instead of a blank screen.
Once it's running, type a request into the box at the bottom, for example "Make a list of 3 fruits with their emojis, and a button to add a new random fruit to the list" The left side shows the rendered UI; the right side shows the raw A2UI JSON the model produced, so you can see exactly what it asked for.
Tip: Tired of typing the long command? Most editors let you save it. In VS Code, add a
launch.jsonconfig with"args": ["--dart-define=FEATHERLESS_API_KEY=your_key_here"].
Here's every meaningful file in lib/ and what it's for. The files you'll edit most are at the top.
| File | What it's for |
|---|---|
lib/catalog.dart |
Defines the widgets the model knows how to use. This is your GenUI vocabulary. It ships with BasicCatalogItems (a ready-made set of common widgets). Add your own components here to expand what the model can build. The catalog feeds both the renderer and the system prompt, so the model can only ever request widgets you've registered. |
lib/prompt.dart |
Defines the overall interaction. A plain system-prompt string: the assistant's persona, tone, and any domain rules. You focus on what the assistant should do; the framework already teaches the model how to emit valid A2UI, so you don't have to. |
Start here. You can build a surprising amount just by editing these two.
| File | What it's for |
|---|---|
lib/model/model_client.dart |
A model-agnostic ModelClient interface. It owns the conversation history and exposes the latest model response. Swap in a different model by writing a new subclass; nothing else has to change. |
lib/conversation.dart |
GenUiSession: the heart of the pipeline. It ties together the GenUI SurfaceController (which renders), the transport (which carries A2UI chunks), the Conversation (which tracks state), and the ModelClient. It builds and disposes all four as a single unit so the UI doesn't have to juggle them. |
| File | What it's for |
|---|---|
lib/home_page.dart |
The main screen. Creates the catalog and session, shows the rendered surface on the left and the raw A2UI source on the right, and feeds your typed messages into the session. |
lib/app.dart |
The root MaterialApp. Theming and top-level app config go here. |
lib/main.dart |
The main() entry point that boots the app. |
lib/widgets/message_input.dart |
The text box and send button at the bottom of the screen. |
lib/widgets/a2ui_source_view.dart |
The right-hand panel that shows the raw A2UI JSON as it streams in. Handy for learning and debugging. |
lib/widgets/widgets.dart |
A barrel file that re-exports the widgets above for tidy imports. |
- Teach the model new tricks. Add a custom component to
lib/catalog.dart. Once it's in the catalog, the model can use it. - Change the personality. Rewrite the string in
lib/prompt.dartto give the assistant a focus, a tone, or domain rules. - Try a different model. Change
_defaultModelinlib/model/featherless_model_client.dart, or write a newModelClientsubclass for a different provider. - Learn the framework. See the
genuipackage on pub.dev for the full catalog API and A2UI format.
Happy building.
Developed with 💙 by Very Good Ventures 🦄