Flutter GenUI Tutorial: Build AI-Powered Interfaces with Flutter

FLUTTER · GENERATIVE AI · 2026

Flutter GenUI: A Complete Tutorial for Building AI-Powered Interfaces

Move beyond chatbot text. Learn how to build Flutter experiences where AI can compose interactive interfaces from an approved catalog of widgets.

Intermediate Flutter & Dart Generative UI

Article type: Technical tutorial | Last reviewed: October 2026

Artificial intelligence is changing how developers design mobile applications. Traditional applications rely on predefined screens and navigation flows. AI-powered applications can go further by adapting the interface to a user's request.

Imagine asking a travel assistant to plan a weekend trip. Instead of receiving a long paragraph, the user sees destination cards, date selectors, budget controls, and action buttons. The interface becomes part of the conversation.

This approach is known as Generative UI (GenUI). In this tutorial, we will explore the Flutter GenUI SDK, understand the A2UI protocol, examine the application architecture, and learn how to connect an AI model to Flutter's widget-rendering system.

What you will learn

  • What Generative UI means and when to use it.
  • How the Flutter GenUI SDK and A2UI work together.
  • How to configure a Flutter project with Firebase AI Logic.
  • How catalogs, surfaces, conversations, and data models fit together.
  • How to connect an AI response to the GenUI transport layer.
  • How to manage generated surfaces and user interactions.
  • Security, performance, testing, and production considerations.

Table of Contents

  1. What is Flutter GenUI?
  2. Traditional UI vs Generative UI
  3. Understanding the architecture
  4. Core GenUI concepts
  5. Set up the Flutter project
  6. Configure Firebase AI Logic
  7. Implement the GenUI integration
  8. Render generated UI surfaces
  9. Create a custom widget catalog
  10. Handle user interactions
  11. Production architecture and security
  12. Performance and testing
  13. Troubleshooting
  14. Frequently asked questions

1. What Is Flutter GenUI?

Flutter GenUI is an SDK for building interactive, AI-driven interfaces in Flutter applications. Instead of displaying only a text response from a language model, an application can render structured UI content using a catalog of widgets that the developer has made available.

The AI does not directly write and execute arbitrary Flutter code on the device. Instead, it produces structured descriptions of UI components. The GenUI renderer interprets those descriptions and maps them to the application's supported widgets.

Example: An AI travel assistant

USER REQUEST

Plan a three-day trip with a budget of $800.

↓

AI-GENERATED INTERFACE

Your Weekend Plan

๐Ÿ“ Destination ๐Ÿ“… Travel dates ๐Ÿ’ฐ Budget: $800
Explore Trip Options

Illustrative interface only; this is not a live GenUI widget.

This pattern is useful for AI assistants, shopping recommendations, task planners, onboarding flows, travel planners, and other experiences where interactive controls are more useful than plain text.

Important status note: The official Flutter GenUI package is in alpha and its APIs may change. Treat this tutorial as a development guide, verify package versions and examples against the current official documentation, and perform a full security review before using it in production.

2. Traditional Flutter UI vs Generative UI

Feature Traditional UI Generative UI
Interface structure Primarily predefined by developers Composed dynamically from approved components
Personalization Usually through predefined branches Can adapt component choices to user intent
Interaction Buttons, forms, and flows designed in advance AI-selected controls can participate in a conversation
Predictability Generally high Requires schema validation and guardrails
Best fit Stable workflows and core navigation Adaptive, conversational, AI-assisted experiences

Generative UI does not replace every conventional Flutter screen. A strong application often combines stable navigation and critical workflows with AI-generated surfaces where flexibility provides real value.

3. Understanding the Flutter GenUI Architecture

Before writing code, it is important to understand the data flow between the user, the AI model, and the Flutter widget tree.

User Input
Natural-language request or UI action
↓
Conversation
Coordinates the conversation lifecycle
↓
AI Agent / LLM
Chooses suitable components and produces structured output
↓
A2UI Transport Adapter
Parses the AI response into UI messages
↓
SurfaceController + DataModel
Manages surfaces, component state, and updates
↓
Flutter Surface
Renders the approved interactive widgets

Conceptual architecture. Exact integration depends on the selected agent provider.

When the user interacts with a generated control, GenUI can update the relevant data model and pass interaction information back into the conversation. The agent can then respond with another UI update.

This creates a loop: user input → AI response → structured UI → user interaction → updated context.

4. Core Concepts You Need to Know

Catalog

Defines which components the AI is allowed to use, their data schemas, and the Flutter builders that render them.

Conversation

Coordinates messages and the interaction between the model, transport, and generated UI.

SurfaceController

Processes UI messages and manages the state and lifecycle of generated surfaces.

A2UI

An open protocol for representing and exchanging structured UI descriptions between agents and UI renderers.

DataModel

Stores dynamic UI data and supports bindings that update widgets when their associated values change.

Surface

A Flutter widget used to display the UI associated with a surface context.

5. Set Up Your Flutter Project

This example uses Firebase AI Logic as the model provider. It is one supported integration option; a server-side A2UI agent is another option for applications with a backend architecture.

Step 1: Create a Flutter project

flutter create genui_travel_demo
cd genui_travel_demo

Step 2: Add the dependencies

flutter pub add firebase_core firebase_ai genui json_schema_builder

The example uses json_schema_builder for the later custom-catalog section. If you only need the basic catalog, you may not need that package immediately.

Step 3: Configure Firebase

  1. Create a project in the Firebase Console.
  2. Install and configure the FlutterFire CLI using the official Firebase instructions.
  3. Run flutterfire configure from your project directory.
  4. Register the platforms you intend to support and verify the generated firebase_options.dart file.
  5. Enable Firebase AI Logic and configure the chosen Gemini API provider in the Firebase Console.

Follow the current Firebase setup guide for your selected platform and AI provider. The setup requirements can change over time.

Security note: Do not put a private model-provider API key directly into Flutter source code. Use a supported managed integration or a trusted backend, and configure authentication, usage restrictions, quotas, and App Check where applicable.

6. Initialize Firebase AI Logic

Initialize Firebase before creating your application. The following example shows the essential startup structure after running FlutterFire configuration.

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

import 'firebase_options.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter GenUI Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.indigo,
        ),
        useMaterial3: true,
      ),
      home: const GenUIHomePage(),
    );
  }
}

The GenUIHomePage screen is implemented in the next section. Keep initialization separate from the screen's UI logic so the project remains easy to maintain.

7. Implement the GenUI Integration

At the heart of the integration are three objects: a Catalog, a SurfaceController, and an A2uiTransportAdapter. The Conversation connects the controller and transport.

Step 1: Import the packages

import 'package:firebase_ai/firebase_ai.dart';
import 'package:genui/genui.dart' hide TextPart;
import 'package:genui/genui.dart' as genui;

The separate genui import prefix avoids a naming collision between the TextPart types exported by the AI SDK and GenUI.

Step 2: Create the catalog and controllers

late final SurfaceController controller;
late final A2uiTransportAdapter transport;
late final Conversation conversation;
late final Catalog catalog;

void initializeGenUI() {
  catalog = BasicCatalogItems.asCatalog();

  controller = SurfaceController(
    catalogs: [catalog],
  );

  transport = A2uiTransportAdapter(
    onSend: sendAndReceive,
  );

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

This is the core wiring pattern. The sendAndReceive function is the bridge to your model provider, which we implement next. Place these fields and methods inside a stateful screen or a dedicated service that manages its lifecycle.

Step 3: Create a system prompt

The model needs clear instructions about the task and the catalog of widgets it can use. PromptBuilder can construct a prompt containing the catalog schema and your application-specific instructions.

const systemInstruction = '''
You are a travel planning assistant.

Help users create a practical travel itinerary.
Use the available UI components when a structured
form, selection, or interactive result is useful.

Keep labels concise and provide helpful explanations.
Never invent confirmed bookings or prices.
Ask for missing details when necessary.
''';

String buildSystemPrompt(Catalog catalog) {
  final promptBuilder = PromptBuilder.chat(
    catalog: catalog,
    systemPromptFragments: [systemInstruction],
  );

  return promptBuilder.systemPromptJoined();
}

In your screen initialization, create the prompt and send it through the conversation:

conversation.sendRequest(
  ChatMessage.system(
    buildSystemPrompt(catalog),
  ),
);

Use a prompt tailored to your application's actual purpose. Prompt instructions guide the model, but they do not replace schema validation, authorization, or application-side safety checks.

Step 4: Connect the model to the transport adapter

The following illustrates the integration pattern with Firebase AI Logic. The response text is passed to GenUI's parser, which processes structured A2UI messages. For production use, adapt this to the current SDK version and handle streaming, cancellation, exceptions, and conversation history as needed.

late final GenerativeModel model;
late final ChatSession chatSession;

void initializeModel(String systemPrompt) {
  model = FirebaseAI.googleAI().generativeModel(
    model: 'gemini-3.5-flash',
    systemInstruction: Content.system(systemPrompt),
  );

  chatSession = model.startChat();
}

Future<void> sendAndReceive(ChatMessage message) async {
  final buffer = StringBuffer();

  for (final part in message.parts) {
    if (part.isUiInteractionPart) {
      buffer.write(
        part.asUiInteractionPart!.interaction,
      );
    } else if (part is genui.TextPart) {
      buffer.write(part.text);
    }
  }

  if (buffer.isEmpty) return;

  final response = await chatSession.sendMessage(
    Content.text(buffer.toString()),
  );

  final text = response.text;
  if (text != null && text.isNotEmpty) {
    transport.addChunk(text);
  }
}

Important: This is an integration example, not a complete standalone application. It assumes the fields and initialization shown in the preceding sections have been wired into the same lifecycle. The model name, Firebase APIs, transport behavior, and streaming support must be checked against the current versions installed in your project. For a streaming response, process response chunks and pass them to transport.addChunk() as they arrive.

8. Render AI-Generated UI Surfaces

GenUI represents generated interfaces as surfaces. Your application listens for surface lifecycle events and creates a Flutter Surface widget for each active surface.

Track surface events

final surfaceIds = <String>[];

void listenToConversation() {
  conversation.events.listen((event) {
    if (!mounted) return;

    setState(() {
      switch (event) {
        case ConversationSurfaceAdded added:
          surfaceIds.add(added.surfaceId);

        case ConversationSurfaceRemoved removed:
          surfaceIds.remove(removed.surfaceId);

        case ConversationContentReceived content:
          debugPrint('AI response: ${content.text}');

        case ConversationError error:
          debugPrint('GenUI error: ${error.error}');

        default:
          break;
      }
    });
  });
}

In a real implementation, keep a reference to the stream subscription and cancel it in dispose(). If you display text responses in a chat history, store them in a message model rather than only printing them.

Display the generated surfaces

ListView.builder(
  itemCount: surfaceIds.length,
  itemBuilder: (context, index) {
    final surfaceId = surfaceIds[index];

    return Surface(
      surfaceContext: controller.contextFor(surfaceId),
    );
  },
)

When the controller processes valid UI messages, the associated surface can update. The rendered components are selected from the catalog available to the application.

Send a user message

Future<void> sendUserMessage(String text) async {
  if (text.trim().isEmpty) return;

  await conversation.sendRequest(
    ChatMessage.user(text.trim()),
  );
}

This sends the user's request into the GenUI conversation. A complete screen should also manage the input controller, loading state, errors, and disposal of all controllers.

9. Create a Custom Widget Catalog

Built-in components are useful for experimentation. For a branded application, custom catalog items let you define exactly how a product card, task list, travel summary, or other domain-specific component should look and behave.

A custom catalog item generally includes three parts:

  1. Schema: Describes the allowed component properties and their types.
  2. Parser: Validates and converts incoming structured data into typed Dart values.
  3. Builder: Renders the typed data as a Flutter widget.

Example catalog design

Imagine a custom DestinationCard with these permitted fields:

  • title — destination name.
  • description — a short summary.
  • priceLabel — display text for an estimated price.
  • action — a declared UI action.

Your schema restricts the shape of the data, your parser validates it, and your builder controls the final visual design.

To define custom schemas, add json_schema_builder and use the current CatalogItem APIs described in the official GenUI examples. The exact constructor signatures can change while the SDK is in alpha.

Professional design tip: Build custom components with consistent typography, spacing, colors, accessibility labels, and responsive behavior. This ensures that AI-generated layouts still feel like part of your application.

10. Handle User Interactions

Generating a button is only one part of the experience. The application must also define what happens when the user taps it.

1. User taps a control
↓
2. Component updates its data or emits an event
↓
3. GenUI sends the interaction context to the agent
↓
4. The agent responds with updated UI or text

For example, a user may select a destination or mark a task as complete. GenUI's event and data-binding mechanisms can communicate the interaction to the agent, which may then update the generated surface.

For sensitive operations such as making a payment, booking travel, deleting data, or changing account settings, the backend must independently validate the request and permissions. Never treat an AI-generated UI event as proof that an action is authorized.

11. Production Architecture and Security

A working demonstration and a production-ready AI application have different requirements. Before releasing a GenUI feature, plan for the following concerns.

Use a controlled widget catalog

Expose only components that make sense for your product. Validate properties, restrict component types, and ensure unknown or malformed data cannot cause unexpected behavior.

Keep business rules outside generated UI

Prices, eligibility, account balances, permissions, and transaction outcomes should come from trusted application services or backend systems. The AI may present the information, but it should not be the authority for business decisions.

Protect credentials and user data

  • Use a supported managed AI integration or a secure backend.
  • Apply authentication and authorization to backend requests.
  • Do not log access tokens, passwords, or sensitive user content.
  • Apply data minimization and retention policies to conversation history.
  • Configure usage limits, quotas, and abuse protection.

Make AI behavior predictable

Use explicit system instructions, schema validation, sensible fallbacks, and application-side checks. AI output is probabilistic, so your UI must handle missing fields, invalid component data, and unexpected responses gracefully.

12. Performance and Testing

Reduce perceived latency

AI model calls can be slower than normal local widget construction. Avoid waiting for an AI response before displaying the entire application shell. Show a useful loading state, keep stable navigation available, and consider caching suitable non-sensitive generated content.

For advanced use cases, investigate the official Flutter team's guidance on asynchronous A2UI generation and caching. Cached results must be invalidated when the underlying data or user permissions change.

Test the experience at multiple levels

  • Unit tests: Validate parsers, schemas, and business logic.
  • Widget tests: Verify catalog components, empty states, and accessibility.
  • Integration tests: Exercise the conversation and surface lifecycle.
  • Failure tests: Simulate timeouts, malformed messages, and provider errors.
  • Security tests: Confirm unauthorized actions are rejected by the backend.

13. Common Problems and Troubleshooting

Problem What to check
Generated UI does not appear Verify the catalog, prompt instructions, transport wiring, incoming messages, and active surface IDs.
The AI returns only plain text Confirm the model prompt includes the catalog instructions and the selected model/provider supports the required interaction.
Custom component fails to render Check component registration, schema properties, parsing logic, and current package API signatures.
AI interaction does not update the UI Inspect data bindings, event definitions, controller updates, and the agent's response handling.
Firebase request fails Check Firebase initialization, provider setup, permissions, App Check configuration, quotas, and network access.
Compilation errors after upgrading Review the GenUI migration notes, resolve compatible dependencies, and compare code with the current official examples.

14. Frequently Asked Questions

What is Flutter GenUI?

Flutter GenUI is an SDK for creating dynamic, AI-driven interfaces in Flutter. It renders structured UI descriptions through an approved widget catalog rather than executing arbitrary AI-generated Dart code.

What is the relationship between GenUI and A2UI?

A2UI is the protocol used to describe and exchange UI structures and updates between an agent and a renderer. The Flutter GenUI SDK implements the client-side components needed to process these messages and render supported widgets.

Does GenUI work with Gemini?

Yes. The official getting-started documentation describes using Firebase AI Logic with Gemini. GenUI also supports alternative agent integration patterns, including a server-side A2UI approach.

Can I use my own Flutter widgets?

Yes. You can define a custom catalog with schemas and builders for your application's own components. This allows you to maintain design consistency and control which UI elements the model can request.

Is Flutter GenUI production-ready?

The official package is in alpha and may change. Evaluate its stability, compatibility, security, testing coverage, and migration requirements before using it for critical production workflows.

Does GenUI replace normal Flutter widgets?

No. Conventional widgets remain appropriate for navigation, stable layouts, forms, and core business flows. GenUI is an additional approach for experiences that benefit from AI-driven composition.

Conclusion

Generative UI introduces a different way to design conversational applications. Instead of limiting AI responses to text, developers can let an agent compose structured interfaces from a controlled catalog of Flutter widgets.

The essential building blocks are the catalog, conversation, transport adapter, surface controller, data model, and surface renderer. Together, these components support an interaction loop in which the AI can respond to user intent with more than words.

For a professional implementation, start with a small use case, use approved widgets, validate generated data, protect backend operations, and measure performance before expanding the experience.

Ready to explore Flutter GenUI?

Read the official documentation and build your first AI-powered interface.

Open Official GenUI Guide

Official Resources

Technical note: This article is an educational overview with integration examples. Package APIs and model availability can change. Verify all code against the current stable Flutter SDK, installed package versions, and official setup guides before deployment.

Comments