Migrating to Flutter's GenUI v0.9: A Step-by-Step Guide

By

Introduction

Generative UI (GenUI) is a design pattern where an AI agent not only generates content but also decides how to present and make it interactive. For Flutter developers, this is made possible through A2UI, an open protocol for agents and clients to collaborate on UI composition. The Flutter team's genui package builds on A2UI, connecting agents with a widget catalog. Recently, both the genui package and A2UI protocol received updates, shifting from a "Structured Output First" approach (where A2UI messages streamed via structured output APIs) to a "Prompt First" approach (agents embed JSON in text responses). This guide walks you through migrating from genui v0.7.0 to v0.9.0, focusing on architectural decoupling, dependency updates, and wiring up new chat loops.

Migrating to Flutter's GenUI v0.9: A Step-by-Step Guide

What You Need

Step-by-Step Migration Guide

Step 1: Update Package Dependencies

Begin by updating the genui package to the latest version. Open your pubspec.yaml file, locate the dependencies section, and change the version constraint for genui to ^0.9.0 (or higher). Then run flutter pub get to fetch the new version. If you have any provider-specific packages like genui_firebase_ai, genui_google_generative_ai, or genui_dartantic, remove them from pubspec.yaml — these are no longer supported in v0.9.0.

Step 2: Clean Up Deprecated Imports

With the old provider packages gone, you'll need to remove any imports referencing them. Search your codebase for lines like:

import 'package:genui_firebase_ai/genui_firebase_ai.dart';

Delete these imports. Also, remove any code that uses ContentGenerator classes — they no longer exist. The framework now uses a decoupled architecture: Engine (SurfaceController), Transport (A2uiTransportAdapter), and Facade (Conversation).

Step 3: Set Up the New Architecture

Replace your old SurfaceController instantiation. Previously, you might have done:

final generator = FirebaseAiContentGenerator();
final controller = SurfaceController(generator: generator);

Now, you'll create a SurfaceController without a generator. Instead, you'll manage the connection to the LLM yourself. Create a transport adapter that streams messages between the agent and your UI. For example:

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

final transport = A2uiTransportAdapter(
  sendMessage: (message) async {
    // Your code to send message to LLM and get response
    return await yourLlm.send(message);
  },
);

final controller = SurfaceController(transport: transport);

You are now responsible for the conversation history, retry logic, and error handling. This gives you full control over the LLM call.

Step 4: Implement the Chat Loop with Conversation Facade

Use the Conversation class to manage chat states. Create a Conversation object that wraps your SurfaceController:

final conversation = Conversation(controller: controller);

// Use the conversation to send user input
await conversation.sendUserMessage('Hello');
// Then listen for responses
conversation.stream.listen((event) {
  // Handle UI updates
});

You can customize how the conversation processes each message, adding your own prompt logic, system instructions, or function calling before sending to the LLM.

Step 5: Wire Up Your LLM Connection

Since the framework no longer wraps the agent, you'll need to integrate your preferred LLM provider directly. For example, using the google_generative_ai package:

import 'package:google_generative_ai/google_generative_ai.dart';

final model = GenerativeModel(model: 'gemini-pro', apiKey: 'YOUR_KEY');
final chat = model.startChat();

// Inside your transport sendMessage:
sendMessage: (message) async {
  final response = await chat.sendMessage(Content.text(message));
  return response.text ?? '';
},

You can add generation config, safety settings, or any custom functions here. The key point: you're no longer constrained by the framework's API.

Step 6: Test and Debug

Run your Flutter app. You should see the UI rendering generated widgets from the agent. Test typical flows: sending messages, receiving responses that include widget definitions, and interacting with those widgets. Common issues include:

Add logging in the transport adapter to see messages flowing. Use the Conversation facade's events to monitor state changes.

Tips

Tags:

Related Articles

Recommended

Discover More

Mastering Long-Horizon Planning with GRASP: A Q&A GuideT-Mobile Reverses Course: Restores Four-Device Promo Limit After BacklashSubnautica 2: Game Pass, Early Access, and Platform Details – Q&ANavigating FDA Regulations on Compounding Obesity Drugs: A Step-by-Step Guide10 Key Facts About the AI-Driven Memory Shortage: Samsung and SK hynix Warn of Extended Scarcity