flutter_ai_elements library

Composable, themeable Flutter UI for AI chat.

Adopts the Vercel AI Elements component vocabulary while rendering through a mobile-first AiThemeExtension — no shadcn or forui dependency. Built from base Flutter widgets so any design system can restyle it via theme tokens.

Presentational vs. bound widgets

  • Presentational (AiMessageBubble, AiConversationView, AiComposer, AiLoader) take plain data and callbacks; reusable and easy to test.
  • Bound (AiChat, AiPromptInput) wire those to a UseChatController from flutter_ai_client for a drop-in chat surface.

Re-exports flutter_ai_client (and transitively flutter_ai_core) so a single import provides the controller, models, and UI.

Classes

AiAnimatedResponse
Reveals a streamed answer with a trailing blur fade-in — the Apple-Intelligence / Siri look — instead of a hard typewriter edge.
AiAttachment
A compact preview of a FilePart attachment.
AiAvatar
A small circular avatar identifying a message's author.
AiBranch
A compact "‹ 2/3 ›" control for navigating between alternate versions of a message (e.g. successive regenerations).
AiChainOfThought
A collapsible, vertical timeline of reasoning steps.
AiChat
A live, drop-in chat transcript bound to a UseChatController.
AiChatView
A batteries-included chat surface: the AiChat transcript above an AiPromptInput, laid out and safe-area-aware. Drop it straight into a Scaffold body — the fastest path from pub add to a working chat:
AiCodeBlock
A monospace code block with a header showing the language and a copy button.
AiComposer
A modern message composer: a rounded input with a leading attach (+) button beside the field, and a trailing pair — a secondary mic and a main button that is Live (voice) while the field is empty and swaps to Send once you type (hiding the mic), or Stop while streaming.
AiConfirmation
An approve/deny card for actions an agent wants to take (running a tool, sending an email, making a purchase) — the human-in-the-loop gate.
AiContextMeter
A compact context-window usage meter: a label, a used / total token readout, and a thin progress bar that turns amber/red as it fills.
AiConversation
An ordered, immutable transcript of AiMessages.
AiConversationList
A ChatGPT-style conversation list / sidebar: a "New chat" action above a scrollable list of ChatThreads, with select and (optional) delete.
AiConversationView
A scrolling list of message bubbles.
AiDataView
Renders a DataPart via a registry, showing fallback (or nothing) when the part's dataType is not registered.
AiEmbedding
A single embedding vector produced by an EmbeddingProvider.
AiEmptyState
A centered placeholder shown when a conversation has no messages yet.
AiErrorBanner
An inline banner surfacing an error, with optional retry and dismiss.
AiImage
Displays an AI-generated (or attached) image with rounded corners, a loading placeholder, an error fallback, and tap-to-zoom into a full-screen, pinch-zoomable viewer.
AiInlineCitation
A small numbered citation badge (e.g. 1) shown inline with text or after a claim, tappable to open or reveal the source.
AiLiveController
Drives a live-voice loop — listen → send → speak → re-listen — by mapping an AiVoiceEngine onto a UseChatController, and exposes the AiLiveSession props (status, amplitude, transcript, muted) as a ChangeNotifier.
AiLiveSession
A full-screen, engine-agnostic Live voice surface, modelled on modern assistant voice modes: a luminous sky-orb that opens centered, then drops and shrinks to dock above the controls while the conversation fades in behind it so you can read along. The orb's interior is an animated, cloud-lit sky that breathes and reacts to audio amplitude.
AiLoader
A three-dot "thinking" indicator shown while the assistant is preparing a response.
AiLocalizations
The user-facing strings used by flutter_ai_elements widgets.
AiLocalizationsDelegate
Serves a fixed AiLocalizations instance. Provide a translated instance to localize, or implement your own delegate to switch by locale.
AiLocalizationsScope
Overrides the AiLocalizations for the widgets below it — the simplest way to translate or customize labels, with no localizationsDelegates wiring:
AiMessage
A single turn in a conversation, authored by one AiRole.
AiMessageActions
A compact row of per-message actions: copy, and optionally regenerate and edit.
AiMessageBubble
A single chat bubble that renders one AiMessage's parts.
AiModelOption
A selectable model option.
AiModelSelector
A compact "model ▾" chip that opens a bottom sheet to switch models.
AiOrb
A small, calm voice/loading orb — a luminous sphere that gently breathes and reacts to audio amplitude. The compact counterpart to the full-screen orb in AiLiveSession, usable inline (e.g. in a composer or status row).
AiPart
A single typed segment of an AiMessage.
AiPromptInput
A composer bound to a UseChatController.
AiReasoning
A collapsible disclosure for the model's reasoning ("chain of thought").
AiRequestOptions
Provider-neutral knobs for a generation request.
AiResponse
Renders a useful subset of Markdown — headings, bold/italic, inline code, fenced code blocks, ordered/unordered lists, blockquotes, and links — with no external dependency.
AiResponseFormat
Requests structured output constrained to a JSON schema.
AiShimmer
An animated shimmer placeholder for pending content — a row of grey bars with a highlight sweeping across them.
AiSources
A wrapped list of citation chips built from SourceParts.
AiStreamEvent
A single incremental update emitted by an LlmProvider during generation.
AiSuggestions
A horizontally scrolling row of tappable suggested prompts.
AiTask
A collapsible "task" card showing a titled checklist the agent works through — each item with a pending/active/complete/error indicator.
AiTaskItem
One line item within an AiTask.
AiThemeExtension
The design tokens that style every flutter_ai_elements widget.
AiThoughtStep
One step in an AiChainOfThought.
AiToolCallSignal
A cancellation signal handed to an onToolCalls executor as its second argument.
AiToolGroup
A vertically stacked list of AiToolInvocation cards — the recommended way to present parallel tool calls.
AiToolInvocation
A collapsible card showing a single tool call: its name, lifecycle state, arguments, and (once available) result.
AiUsage
Token usage for a model turn, with an optional cost estimate.
AiVoiceEngine
The audio side of a live voice session: speech-to-text in, text-to-speech out. Implement it over your engine of choice (speech_to_text + flutter_tts, a realtime API, …); AiLiveController drives the rest.
AiWidgetRegistry
A name→widget allowlist for generative UI: the model emits a DataPart with a dataType discriminator and a JSON payload, and the registry maps it to a Flutter widget.
ChatObserver
Observes the agent lifecycle of a UseChatController for tracing, metrics, and logging.
ChatStore
Persists and restores AiConversations so a chat survives app restarts.
ChatThread
A lightweight summary of a stored conversation, for a thread list / sidebar.
ChatThreadStore
A ChatStore that can also enumerate and delete threads — enough to drive a conversation list / sidebar.
DataPart
A structured data payload that drives generative UI.
EmbeddingProvider
An optional capability a provider MAY implement to turn text into embedding vectors (for semantic search, clustering, and RAG retrieval).
FilePart
A file attachment: an image, document, or audio clip.
InMemoryChatThreadStore
An in-memory ChatThreadStore — handy for demos, tests, and prototyping before wiring real storage. Titles are derived via autoTitle on save.
JsonAccumulator
Accumulates a JSON document that arrives in fragments and parses it tolerantly while still incomplete.
KeyValueChatThreadStore
A persistent ChatThreadStore backed by any KeyValueStore, so a chat drawer survives app restarts without pulling a storage plugin into the package. Each conversation is stored as JSON under "$prefix$id", with a small index under "${prefix}index" for listThreads. Titles are derived via autoTitle on save.
KeyValueStore
A minimal async key→string storage — the seam a KeyValueChatThreadStore persists through. Keeps the package plugin-free: back it with shared_preferences, a file, secure storage, or an HTTP API in a few lines:
LlmProvider
The contract every model backend implements: turn a conversation into a stream of incremental AiStreamEvents.
MarkdownTextRenderer
An AiTextRenderer that renders Markdown via AiResponse. The default renderer for assistant content.
MessageFinished
Marks message messageId complete, carrying the reason generation ended.
MessageProcessor
Folds a stream of AiStreamEvents into evolving AiConversation state.
MessageStarted
Announces a new message and its author, before any content arrives.
MutationResult
The outcome of applying one stream event to a MessageProcessor.
PartReceived
Appends a fully-formed part (a file, source, or data payload) to message messageId.
PlainTextRenderer
A renderer that emits a plain Text widget (opt in; the widgets default to MarkdownTextRenderer).
ReasoningDelta
Appends delta to the reasoning of message messageId.
ReasoningPart
The model's intermediate reasoning ("chain of thought").
SourcePart
A citation or source referenced by the model, rendered as a link or chip.
StreamErrorEvent
Reports an error during generation.
TextDelta
Appends delta to the prose of message messageId.
TextPart
Human- or model-authored prose, typically rendered as Markdown.
TextRenderer<T>
A strategy for turning message text into a rendered representation.
TokenCounter
An optional capability a provider MAY implement to count the tokens a request would consume before sending it.
ToolCallDelta
Appends a fragment of argument JSON to tool call toolCallId.
ToolCallPart
A request from the model to invoke a tool.
ToolCallReady
Signals that tool call toolCallId has received all its arguments.
ToolCallStarted
Opens a tool call within message messageId.
ToolDefinition
A declaration of a tool the model may call: its name, purpose, and the JSON Schema describing its arguments.
ToolResultPart
The output of a tool, fed back to the model and shown to the user.
ToolResultReceived
Delivers the output of tool call toolCallId into message messageId.
UseChatController
Drives a chat conversation against any LlmProvider, exposing state as a Listenable (this class is a ChangeNotifier).

Enums

AiConfirmationTone
The visual weight of an AiConfirmation, which restyles its confirm button.
AiLiveStatus
The phase of a live voice session.
AiMessageActionKind
The per-message actions, used to control ordering via AiMessageActions.order and AiMessageActions.trailing.
AiMessageStatus
The delivery state of an AiMessage.
AiMessageStyle
How an assistant message is laid out.
AiRole
The author of a message in a conversation.
AiTaskStatus
The state of an AiTaskItem.
ChatStatus
The lifecycle state of a chat turn driven by a controller.
FinishReason
Why the model stopped generating a message.
ReasoningEffort
How much effort a reasoning-capable model should spend on internal thinking before answering.
ToolCallState
The lifecycle stage of a single tool call.

Extensions

GenerateObject on LlmProvider
Structured-output helpers layered on top of any LlmProvider.

Functions

attachStore(UseChatController controller, ChatStore store, String id, {Duration debounce = const Duration(milliseconds: 400)}) VoidCallback
Auto-saves controller's conversation to store under id whenever it changes and the turn has settled, coalescing rapid changes over debounce.
autoTitle(AiConversation conversation, {String fallback = 'New chat', int maxLength = 40}) String
Derives a short title from a conversation's first user message, falling back to fallback. Trims to maxLength characters.
keepLastMessages(int count) AiConversation Function(AiConversation)
History-trimming strategies for UseChatController.trimHistory.
keepLastWithSummary({required String summary(), required int count, String summaryLabel = 'Summary of earlier conversation:'}) AiConversation Function(AiConversation)
Keeps the system prefix plus a rolling summary of older turns, plus the most recent count non-system messages.
llmExceptionFor(int status, String body, {Duration? retryAfter}) LlmException
Maps an HTTP status to the matching LlmException subtype.
showAiMessageActions(BuildContext context, {required AiMessage message, VoidCallback? onCopy, VoidCallback? onRegenerate, VoidCallback? onEdit}) Future<void>
Presents the per-message actions in a native bottom sheet — the idiomatic mobile pattern, triggered from a long-press on a message.
suggestFollowUps(AiConversation conversation, LlmProvider provider, {int count = 3, AiRequestOptions? options}) Future<List<String>>
Generates up to count short follow-up prompts a user might send next, given the current conversation, via a one-off call to provider.
trimToApproxTokenBudget(int maxTokens, {int charsPerToken = 4}) AiConversation Function(AiConversation)
Keeps the system prefix plus as many of the most recent non-system messages as fit within maxTokens, estimated from text length.
validateJsonSchema(Object? value, Map<String, Object?> schema, {String path = 'args'}) List<String>
Validates value against schema, returning a list of violation messages (empty when valid). path names the root in messages (defaults to args).

Typedefs

AiDataWidgetBuilder = Widget Function(BuildContext context, Map<String, Object?> data)
Builds a widget for a DataPart's payload.
AiTextRenderer = TextRenderer<Widget>
A TextRenderer that produces a Flutter Widget — the rendering seam used throughout the UI.
CodeHighlighter = List<TextSpan>? Function(String code, String? language, TextStyle base)
Turns code in language into styled spans for syntax highlighting, using base as the baseline text style (family/size/default color).

Exceptions / Errors

AgentLoopException
Surfaced on UseChatController.error when the agent loop is halted because the model requested the same tool call (identical name + args) more than the controller's maxIdenticalToolCalls limit — a runaway-loop guard that stops the turn instead of looping (and spending tokens) up to maxSteps.
AiToolCallCancelled
Thrown by AiToolCallSignal.throwIfCancelled when the turn was cancelled.
LlmAuthException
Authentication/authorization failure (HTTP 401/403) — usually a bad or missing API key.
LlmException
A failed provider HTTP request, surfaced on StreamErrorEvent.error so hosts can branch on the type (auth vs. rate-limit vs. server) instead of string-matching a message.
LlmRateLimitException
Rate limited (HTTP 429). Honor retryAfter before retrying.
LlmRequestException
A non-retryable client error (other 4xx) — e.g. a malformed request.
LlmServerException
Server-side failure (HTTP 5xx, incl. Anthropic 529 overloaded).