Skip to main content
Back to Blog
mobilefluttergenerative-uia2uicross-platform-developmentagentic-uillm-integration

Flutter's GenUI Pivot: Why Google Just Traded 'Structured Output' for Plain Text Prompts

Flutter's A2UI v0.9 abandons structured output for plain text prompts. Learn how the Flutter GenUI pivot reshapes agentic UI for cross-platform mobile apps.

Zyfolks Team ·

Google’s Flutter team just walked back one of the most aggressive bets in agentic mobile UI — the idea that LLMs should be hard-wired through structured output APIs to generate interface schemas. With the v0.9 release of A2UI and the matching update to the genui package, that contract is gone. The framework now asks agents to write JSON as plain text inside their responses, and it asks you, the developer, to wire up the LLM connection yourself. For a community that has spent a decade arguing about whether cross-platform frameworks hide too much or too little, this is a real reversal.

How A2UI v0.9 Reframes Generative UI on Mobile

The Flutter team describes GenUI as a pattern where an agent decides not just what content to show, but how it should be displayed and made interactive. A2UI is the open protocol that defines how agents and renderers negotiate that UI, and genui is Flutter’s package implementation. According to the release notes, v0.9 shifts the framework from a “Structured Output First” philosophy — where A2UI messages were streamed through provider-specific structured output APIs — to a “Prompt First” approach where agents emit JSON blocks as text.

Why this matters: structured output mode locks you into whatever JSON-mode or function-calling primitives your LLM provider supports. Switch providers, and you rewrite the integration. Worse, the Flutter team admits that deeply nested schemas “could sometimes confuse models or fight against their natural text-generation tendencies,” and that catalog size was effectively capped by what the provider’s structured output endpoint could digest.

Imagine you’re a team shipping a cross-platform shopping app that uses an agent to render product cards, filters, and checkout flows on the fly. Under v0.7, swapping from Gemini to a self-hosted model meant waiting for a provider-specific wrapper package. Under v0.9, you hand the agent a system prompt built by PromptBuilder, parse the text response, and feed chunks into an A2uiTransportAdapter — provider-agnostic by design.

My take: this is Flutter conceding that the most durable interface between agents and clients in 2026 is still text, not vendor SDKs. That’s the right bet.

Why Decoupling ContentGenerator Is Bigger Than It Looks

The headline architectural change is the removal of ContentGenerator, the class that previously hid prompt construction, network calls, and response parsing behind a single facade. The framework now splits into three explicit layers: a SurfaceController engine that manages UI state, an A2uiTransportAdapter transport that streams messages, and a Conversation facade for chat state. The provider-specific wrappers — genui_dartantic, genui_google_generative_ai, genui_firebase_ai — are gone from the package tree.

The practical cost: you write more wiring code. The Flutter team’s own example shows the new onSend callback iterates message parts, builds a buffer, calls myAgentClient.sendRequest, and pumps the response back via adapter.addChunk. That’s a dozen-ish lines of plumbing where there used to be three. But you get genuine control in return — over model choice, lifecycle, retry, history, and testing. As the post notes, genui now accepts tokens directly, “and they can come from an agent, or a mock agent, or a hard-coded test.”

If you’re a studio running cross-platform mobile builds for multiple clients on the same codebase, this matters a lot. One client wants Gemini, another wants Claude, a third insists on an on-prem model behind their VPN. Under the old API, you were rebuilding the bridge for each. Under v0.9, the bridge is your own code, and genui only cares about tokens in and tokens out.

My take: Flutter just got there faster than most.

The Prompt-First Trade-Off and What It Costs You

The PromptBuilder tool is the package’s answer to the obvious worry: if the schema isn’t enforced at the API level anymore, who guarantees the agent produces valid A2UI? The Flutter team’s argument is that modern LLMs are “highly optimized to follow detailed system instructions and examples,” and that plain-text schemas align with how they reason. PromptBuilder.chat takes a catalog plus your own system prompt fragments and emits a full system prompt string you can pass to any LLM.

The upside is debuggability — the constraints now live in a prompt you can read, version, and tweak, rather than buried in network-layer parameters. The downside is that responsibility for getting the right prompt into the context window now belongs to your app. If you forget to include the schema, or you truncate it during compaction, the agent will hallucinate UI components that don’t exist in your catalog.

Picture a team building an Android shopping client where the catalog includes 40+ widget types — product cards, carousels, filter chips, payment sheets. With a prompt-first design, that schema is going into every system prompt, which means token cost and context pressure scale with catalog size. Teams will need to think hard about catalog discipline, prompt caching strategies, and whether to dynamically narrow the catalog per turn.

My prediction: within two release cycles, we’ll see PromptBuilder gain a “dynamic catalog” mode that ships only the widget subset relevant to the current conversation turn, because catalog-scaled token costs will bite production teams first.

Breaking Changes and the Rename Tax

A2UI v0.9 also ships breaking protocol changes that will trip up anyone hand-constructing JSON. beginRendering is now createSurface. Components dropped nested keys ({"Text": {"text": "Hello"}}) in favor of flat discriminators ({"component": "Text", "text": "Hello"}). Data binding is simplified to {"path": "/path/to/var"}. Property names got cleaned up too: distributionjustify, alignmentalign, usageHintvariant, text (in TextField) → value, and userActionaction.

The Dart-side rename is just as broad. The GenUi prefix is gone from almost every core class: GenUiConversationConversation, GenUiControllerSurfaceController, GenUiSurfaceSurface, GenUiHostSurfaceHost, GenUiContextSurfaceContext, GenUiTransportTransport, and GenUiFallbackFallbackWidget. CoreCatalogItems is now BasicCatalogItems to signal it’s a baseline, not a requirement. And GenUiFunctionDeclaration plus references to “tools” became ClientFunction to match standard LLM function-calling terminology.

Messaging types moved out of genui entirely into a new genai_primitives package that ships ChatMessage, MessagePart, and ToolDefinition — primitives meant to be reusable across any GenAI Dart application, not just GenUI.

If you’re already running v0.7 in production, budget time for this. The rename tax alone is non-trivial, and the flat component discriminator change means any serialized state you’ve persisted from earlier versions is incompatible. Teams building AI-integrated product features on top of GenUI should pin versions carefully and migrate behind a feature flag.

My take: the rename to Surface, Transport, and Conversation signals that Google wants A2UI to be the protocol the rest of the ecosystem builds against, not a Flutter-only convention. Expect React Native and Compose Multiplatform renderers to surface in the next twelve months.

FAQ

Q: What is A2UI and how does it differ from MCP? A: A2UI is an open protocol that defines how an agent and a client renderer collaborate on the composition and state of a user interface. Where MCP standardizes how agents call tools and fetch context, A2UI standardizes how agents describe UI for clients to render. They’re complementary layers in the agentic stack.

Q: Should I migrate from genui v0.7 to v0.9 right now? A: If you have production users, wait until your prompt-first wiring is tested behind a flag — the removal of ContentGenerator and the flat component schema are breaking, and the Flutter team’s migration guide explicitly covers dependency cleanup and chat-loop wiring. If you’re prototyping, jump straight to v0.9 because the architecture is what the project will build on.

Q: Does prompt-first generative UI work with non-Google LLMs? A: Yes — that’s the point. Because the schema and instructions are injected as plain text via PromptBuilder, you can connect to any provider’s API in your own onSend callback. The framework no longer wraps your agent, so the LLM choice, generation settings, and custom functions are all yours to manage.

Key Takeaways

  • Teams building cross-platform GenUI apps should treat v0.9 as the new baseline; v0.7 is now a dead-end branch architecturally.
  • Catalog size will become a token-cost problem in prompt-first mode — start designing widget catalogs with prompt budget in mind, not just runtime flexibility.
  • The decoupled SurfaceController / TransportAdapter / Conversation split makes provider-agnostic mobile agent apps practical for the first time; lock-in to a single LLM vendor is now a choice, not a default.
  • Expect non-Flutter A2UI renderers to appear within a year, which means generative UI logic written today against the v0.9 protocol may travel to other client platforms with minimal rewrites.
  • The genai_primitives package is a quiet hint that Google wants a shared Dart-level vocabulary for GenAI apps; watch it become a dependency of more than just genui.

Have a project in mind?

Tell us what you're building — we reply within 24 hours.