Message & Thread
Conversation rendering from plain message data — roles, streaming, and the pending and error states.
FlowThread renders a conversation from plain message data — user,
assistant, and system roles — and FlowMessage renders a single turn: an
ink-wash bubble for the user, plain text on the page for the assistant.
Neither knows where the messages came from.
A conversation
Section titled “A conversation”FlowThread( messages: [ FlowMessageData.text( id: '1', role: FlowMessageRole.user, text: 'What is flow_ui?', ), FlowMessageData.text( id: '2', role: FlowMessageRole.assistant, text: 'An open-source Flutter UI library to build production-grade Chat & AI assistant interfaces...', ), FlowMessageData.text( id: '3', role: FlowMessageRole.system, text: 'Model changed to Sonnet', ), ],)Streaming
Section titled “Streaming”Streaming is data, not streams: as chunks arrive, rebuild with the updated
message list and the thread animates the reveal. Status walks
pending → streaming → complete.
FlowThread( messages: [ ...history, FlowMessageData( id: 'reply', role: FlowMessageRole.assistant, parts: [FlowTextPart(streamedSoFar)], status: FlowMessageStatus.streaming, ), ],)Pending and error states
Section titled “Pending and error states”A pending assistant message shows the thinking indicator until the first
token arrives; an error status wraps the content in an error bubble.
// Pending: the thinking indicator until the first token arrives.FlowMessage( FlowMessageData( id: 'p', role: FlowMessageRole.assistant, status: FlowMessageStatus.pending, ), thinkingLabel: 'Thinking…',)
// Error: content in an error bubble.FlowMessage( FlowMessageData.text( id: 'e', role: FlowMessageRole.assistant, text: 'Something went wrong while generating a response.', status: FlowMessageStatus.error, ),)Message parts
Section titled “Message parts”Message content is typed parts, not strings: a sealed FlowMessagePart
with FlowTextPart, FlowAttachmentPart, and FlowCustomPart subtypes.
FlowCustomPart plus a FlowCustomPartBuilder on the message is the
extension seam — it carries a type your builder switches on and an
arbitrary data payload, so the host injects its own content (a tool
card, a chart) without the library knowing what it is.
Key classes
Section titled “Key classes”FlowThread— the scrolling conversation; give it aScrollControllerto pair withFlowChatScreen’s jump-to-latest.padding(the design’s 16) anditemSpacing(32) override the metrics;messageBuilderswaps the defaultFlowMessageper turn;thinkingLabel,charactersPerSecond, andpreviewCloseTooltipforward to every message.FlowMessage— one turn; accepts aFlowCustomPartBuilder, anonAttachmentTapoverride,leadingandfooterslots (an avatar, the action row), atextStyle, and the user bubble’s overrides —maxBubbleWidthFraction(0.75),bubbleRadius,bubblePadding.FlowMessageData—id,role,parts,status, and an optionaltimestamp; the.textconstructor covers the plain case.