Skip to content

feat: attachment input, the attach affordance and drop target #25

Description

@divyanshub024

What kind of request is this

A variant or option on an existing component

The problem

The composer can show attachments but offers no way to add them. FlowComposer.attachments renders the strip, remove and preview work, and that is the whole story: there is no attach button, no drag and drop, no paste. Hosts wedge an "Add" option into a FlowMenu (the playground does exactly this) and build the rest themselves. Attaching a file is a core chat gesture, and the package is silent on it.

The constraint to design around: file picking and OS drop events live in platform plugins (file_picker, image_picker, desktop_drop), and the package ships zero third-party dependencies. So flow_ui cannot pick files. It can own the affordances, the drop-state visuals, and the intent seams, and let the host own the platform work. State in, intent out, as everywhere.

What you'd like

Three pieces, each independently useful:

1. An attach affordance on the composer. A first-class paperclip in the action bar that reports intent. The host opens whatever picker it likes and passes the result back as attachments.

FlowComposer(
  onSend: send,
  onAttach: pickFiles,          // null hides the affordance, as with onStop
  attachTooltip: 'Add files',   // host-localized, doubles as the accessible name
  attachments: picked,          // state in, as today
)

2. The drop-target treatment on the chat surface. The visual half of drag and drop: a highlight state over the composer (or the whole FlowChatView) with a host-written label ("Drop files to attach"). Driven by host state, so it works with whatever drop detection the host has:

FlowChatView(
  dropActive: dragging,          // host flips it from its drop region
  dropLabel: 'Drop files to attach',
  composer: ...,
)

3. Native drop and paste where the SDK allows. If Flutter's own APIs can detect OS file drops or clipboard images on a given platform without a plugin, wire them to an onDropFiles / onPasteFiles intent so the host only handles the data. Where the SDK cannot, pieces 1 and 2 still make the host's plugin integration a few lines.

Decisions to settle

  1. Where the drop treatment lives: composer-only (files land in the input) or the whole chat surface (the larger target every desktop chat app uses). Leaning whole surface, rendered by FlowChatView.
  2. What crosses the intent seam for drops and paste: platform file handles are plugin territory, so probably nothing. The host detects the drop, reads the files, and passes FlowAttachments in. Piece 3 is only worth building if the SDK genuinely covers a platform.
  3. Whether the attach affordance joins leadingActions as a packaged widget (FlowAttachButton) or becomes a built-in slot on the composer like send/stop. Leaning built-in: it should sit in the design's position without host layout work.
  4. Style: fold the affordance's colors into FlowComposerStyle, and the drop overlay's into a couple of new fields there or on the chat view. No new style class unless the overlay grows.

Accessibility

The attach affordance takes attachTooltip as its accessible name. The drop overlay announces when it appears. Keyboard users get the same path as everyone: the affordance is focusable and the picker flow never depends on dragging.

Definition of done

  • FlowComposer.onAttach + attachTooltip, affordance hidden when null
  • Drop-state treatment (dropActive + dropLabel) on the agreed surface
  • SDK-native drop/paste intents where possible, documented per platform (decision 2)
  • Style fields per decision 4, following the component-styles convention (feat: component styles on every widget with app-wide FlowTheme defaults #21)
  • No shipped strings; no new dependencies
  • Playground demo: attach intent readout, a toggle that fakes dropActive
  • Docs: composer and chat-view pages updated, with a short host recipe wiring file_picker and a drop plugin through the seams
  • Dogfooded in the example's Gemini flow (attach an image, send it with the prompt)

What you're doing instead

An "Add to Chat" FlowMenu in leadingActions plus a hand-rolled picker and drop region in the host, with no drop-state visuals. Works, but every host rebuilds the same affordance and overlay, and the composer looks incomplete next to any production chat app. For reference: assistant-ui, ChatGPT, and Claude all ship the paperclip plus a full-surface drop target.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions