Attachments
Attachment tiles for the composer and sent messages, with a full-screen preview.
FlowAttachment models one attachment — its id, an optional
thumbnail, the file kind shown in the type pill, and a label.
FlowAttachmentGroup lays a set of them out for the composer or a
message. A tile with an image opens the full-screen preview — zoom,
paging — on tap; one without, a document, draws its ground and the pill
and stays inert rather than opening an empty preview. Tiles stand at the
design’s 80px on a 16px corner — 64 and 12 on phones, resolved from the
theme’s platform.
Scrolling row
Section titled “Scrolling row”The default layout is one row that scrolls, no scrollbar. With onRemove
each tile grows a remove button — it fades in on hover and stays visible
on touch:
FlowAttachmentGroup( // The default: one row that scrolls, no scrollbar. attachments: [ for (final picked in pending) FlowAttachment( id: picked.id, thumbnail: FileImage(picked.file), kind: picked.extension, label: picked.name, ), ], onRemove: (id) => detach(id), removeTooltip: 'Remove attachment',)Documents
Section titled “Documents”No thumbnail means no preview — the pill identifies the file:
FlowAttachmentGroup( layout: FlowAttachmentLayout.wrap, attachments: [ // No thumbnail: the tile draws its ground and the type pill, and // stays inert rather than opening an empty preview. FlowAttachment(id: 'c', kind: 'PDF', label: 'contract.pdf'), FlowAttachment(id: 'n', kind: 'DOCX', label: 'notes.docx'), FlowAttachment( id: 'd', thumbnail: AssetImage('assets/dusk.png'), kind: 'PNG', label: 'dusk-ridge.png', ), ],)Read-only
Section titled “Read-only”Without an onRemove callback the tiles are read-only. Tapping an image
still opens the preview — pass onTap only to replace that, and call
showFlowAttachmentPreview yourself if you want the preview alongside
your own handling:
FlowAttachmentGroup( layout: FlowAttachmentLayout.wrap, // No onRemove → read-only. Tapping still opens the preview: pass onTap // only to replace it, and call showFlowAttachmentPreview yourself to // keep it alongside your own handling. attachments: sent,)In a message
Section titled “In a message”Attachments travel as message parts: FlowAttachmentPart in
FlowMessageData.parts. Nothing to wire — tapping a sent attachment
opens the preview:
FlowMessage( FlowMessageData( id: 'm1', role: FlowMessageRole.user, parts: [ FlowAttachmentPart([ FlowAttachment(id: 'a', thumbnail: NetworkImage(url), kind: 'JPG'), ]), FlowTextPart('What is the peak on the left?'), ], ), // Nothing to wire: tapping a sent attachment opens the preview.)In a composer
Section titled “In a composer”The composer renders the pending row above its field from the same model:
FlowComposer( attachments: attachments, onRemoveAttachment: (id) => setState( () => attachments = attachments.where((a) => a.id != id).toList(), ), removeAttachmentTooltip: 'Remove attachment', onSend: send, leadingActions: [ FlowMenu(icon: Icons.add, entries: addOptions, onSelected: pickAttachment), ],)Key API
Section titled “Key API”FlowAttachment—id,thumbnail(anyImageProvider),preview(a full-resolution image for the viewer; defaults tothumbnail),kind,label, andtooltip(falls back tolabel).FlowAttachmentGroup—attachments,layout(FlowAttachmentLayout.scrolldefault /.wrap),onRemove,onTap, and the host-supplied tooltips, plus the tile metrics:size(80; 64 on phones),tileRadius(16; 12),spacing(8),padding. Callbacks report ids, never indexes, so ids must be unique within the group — asserted in debug.showFlowAttachmentPreview— the full-screen viewer, callable directly when you overrideonTaporonAttachmentTap. Escape closes it and the arrow keys page.