Skip to content

FlowConfirmation is the approval card: an asterisk-marked request on a raised card, with approve and reject buttons that settle into the outcome. It renders state and reports intent; recording the decision, and re-rendering the card settled, is the host’s business. The package ships no strings, so the title, the request and every button label are host-localized; the request announces to assistive tech as a live region, since it arrives unprompted.

The card is runtime chrome, not content: the asterisk header and the raised, hairline-and-shadow surface are its identity, the mark that distinguishes the host’s own gate (a destructive tool call, a guarded action) from anything composed into the conversation. That is why the glyphs are fixed and only their colors restyle.

The default state, straight from the design: the warning accent, the request, and the two buttons. A button renders only when both its label and its callback are set; a pending card with neither pair is a read-only notice, for chrome staged before the request is answerable.

The full anatomy
FlowConfirmation(
title: 'Approval required',
message: 'Delete 3 files in drafts/. This cannot be undone.',
approveLabel: 'Approve',
rejectLabel: 'Reject',
onApprove: () => respond(true),
onReject: () => respond(false),
)

The widget holds no state: a tap reports intent, and the card settles only when the host passes the new status back. The buttons collapse into one row of the same footprint (so the card’s height holds) and the accent flips to success:

The settled card
FlowConfirmation(
title: 'Approval required',
message: 'Delete 3 files in drafts/. This cannot be undone.',
status: FlowConfirmationStatus.approved,
approvedLabel: 'Approved',
)

The rejected outcome carries the error accent:

The declined request
FlowConfirmation(
title: 'Approval required',
message: 'Delete 3 files in drafts/. This cannot be undone.',
status: FlowConfirmationStatus.rejected,
rejectedLabel: 'Rejected',
)

A FlowConfirmationPart in any turn becomes this card. The buttons hand the message, the part and the decision back through FlowThread.onConfirmationRespond, so the host can find the request they belong to and re-render the part settled; the labels are thread-level, since they are the same words on every card. Keep the message’s own status complete while the confirmation is pending; the wait belongs to the part:

The host contract
FlowThread(
messages: messages,
approveLabel: 'Approve',
rejectLabel: 'Reject',
approvedLabel: 'Approved',
rejectedLabel: 'Rejected',
// Typically: record the decision, run or skip the action, and
// re-render the part with the settled status.
onConfirmationRespond: (message, part, approved) =>
record(message, part, approved),
)
FlowMessageData(
id: 'a1',
role: FlowMessageRole.assistant,
parts: [
FlowTextPart('I can clear those drafts for you.'),
FlowConfirmationPart(
title: 'Approval required',
message: 'Delete 3 files in drafts/. This cannot be undone.',
),
],
)

FlowConfirmationStyle carries the card’s overrides. Install one on FlowTheme.confirmationStyle for every card, or pass style: to one widget; a widget’s own style wins field by field, and nulls fall through to the tokens. The three accents color a state’s marks (the asterisk, the settled glyph and its wash) so one override recolors it coherently. The title and the settled label stay in the ink ramp: the light accents fall short of WCAG AA for text on the card, so the words keep their contrast and the accent signals beside them:

A different pending accent
FlowConfirmation(
title: 'Approval required',
message: 'Delete 3 files in drafts/?',
style: const FlowConfirmationStyle(pendingColor: Color(0xFFB65C33)),
)

Beyond the style class, padding: and borderRadius: override the card’s own metrics, the per-component convention.

Member What it does
title / message Host-localized header and request; the message is a live region
status FlowConfirmationStatus.pending shows the buttons; approved / rejected settle the card
approveLabel / onApprove The filled button; renders only when both are set
rejectLabel / onReject The outlined button; same rule
approvedLabel / rejectedLabel The settled row’s text; null leaves the outcome glyph alone
style FlowConfirmationStyle overrides, merged over FlowTheme.confirmationStyle