SelectionArea
Enables app-wide text selection in its subtree.
Learn more: Input & gestures
Import: package:fleury/fleury.dart, or package:fleury/fleury_core.dart in browser code.
SelectionArea( onSelectionChanged: (content) => setState(() => selected = content?.plainText ?? ''), child: const Column( children: [ Text('Planning notes'), Text('Meet on Tuesday.'), ], ),)Details
Section titled “Details”Wraps a subtree in:
- A
SelectionScopethat publishes aSelectionContainerDelegate, so descendant render objects (e.g.RenderText) can register themselves asSelectable. - A
GestureDetectorthat turns mouse drag intoSelectionEdgeUpdateEvents. - A
KeyBindingsblock providing Ctrl+A (select all), Ctrl+C (copy via the ambientClipboardScope), and Esc (clear). Bindings bubble when there’s nothing to act on, so ancestors still see the event.
Mouse drag. Press, drag, release. Every Text under the area
highlights its share of the selection automatically; the full
selected text crosses widget boundaries with newlines inserted at
row transitions.
Keyboard. Ctrl+A selects everything below the area; Ctrl+C
pushes the selection through the ambient clipboard’s write (which
tries platform tools, OSC 52, and an in-process register in that
order); Esc clears.
Selecting text gives the region keyboard focus. It does not autofocus or
add a Tab stop by default; nested controls keep their own focus behavior.
Supply focusNode to control focus or opt the region into traversal.
Auto-copy. Set copyOnRelease: true to fire a clipboard
write on every mouse-release that completes a non-empty drag —
matches the “select to copy” idiom of native terminals.
Multi-click. Double-click selects the word at the click position (alphanumerics + Unicode letters/digits/underscore, with the punctuation-or-whitespace at the click position falling back to a single character). Triple-click selects the entire laid-out line at the click point.
Shift+Arrow. Extends the moving edge of an existing selection by one grapheme (left/right) or one row (up/down). Wide characters (CJK, emoji, ZWJ sequences) are crossed in one step, and vertical motion hops between Selectables so Shift+Down can cross from one Text into another. When no selection is active the Shift+Arrow event bubbles so a different consumer (focus traversal, scroll) can claim it.
Shift+Home / Shift+End. Extends the moving edge to the start or end of the cursor’s current row (the Selectable it sits in), keeping the anchor — so Shift+End selects through the last character of the line. Like Shift+Arrow, this bubbles when no selection is active.
Shift+Click. When a selection is active, holding Shift while clicking moves the cursor to the click point without disturbing the anchor — extends the selection to the click. Shift+Click also breaks the double/triple-click streak so a Shift+Click after a double-click starts a fresh single selection from the anchor. With no active selection, Shift+Click falls through to the normal “fresh anchor” path.
Opt-out. For a subtree that should NOT participate in any
ancestor SelectionArea’s selection, wrap it in
SelectionArea.disabled. For an individual widget, set
allowSelect: false — supported on Text and RichText.
Selectable widgets. Text and RichText are selectable today.
A mixed subtree (plain Text next to styled RichText next to more
plain Text) selects across all of them — the clipboard copy is
the plain text with \n between rows, never ANSI escape codes.
Shift-bypass for native selection. On terminals that support it (xterm, GNOME Terminal/VTE, Konsole, Kitty, WezTerm, Ghostty, Windows Terminal), holding Shift while clicking bypasses the app’s mouse reporting and uses the terminal’s own native selection. This requires no code on our side — the terminal intercepts the event before it ever reaches the app. iTerm2 uses Option instead. Alacritty historically didn’t support this; check per-terminal docs.
macOS Terminal.app limitation. Apple’s Terminal.app does not implement OSC 52, so payloads from the OSC 52 fallback path are silently dropped there. The platform-tool path (pbcopy) is used when not over SSH, but cross-machine clipboard from SSH on Terminal.app does not work. Recommend iTerm2, Ghostty, or Kitty for that platform.
Constructors
Section titled “Constructors”SelectionArea()
Section titled “SelectionArea()”| Parameter | Type | Default | Description |
|---|---|---|---|
focusNode: | FocusNode? | — | Optional caller-owned focus node for this region. Without one, the area manages a node that receives pointer focus but is skipped by Tab. More |
onSelectionChanged: | SelectionChangedCallback? | — | Called whenever the active selection changes. Receives a SelectedContent (whose plainText is the concatenated selection across all Selectables in reading order), or null when nothing is selected. |
copyOnRelease: | bool | false | When true, the active selection is pushed to the ambient ClipboardScope on every mouse-release that completes a non-empty drag. Off by default — most apps want explicit Ctrl+C wiring or a different trigger. |
copyClearsSelection: | bool | false | When true, a Ctrl+C copy clears the selection after writing it. Off by default (an explicit SelectionArea keeps the selection visible so you can copy again). The app-wide default wrap (DefaultRootSelection) turns it ON: there, Ctrl+C also means quit (the runApp exit guard), so a held selection must not trap Ctrl+C on “copy” — clearing after the copy lets the next Ctrl+C bubble through to quit. |
selectAllShortcut: | bool | true | Whether to bind Ctrl+A to select-all. On by default. The app-wide default wrap (DefaultRootSelection) turns it OFF so the always-on selection never shadows an app’s own Ctrl+A binding (a deeper binding out-ranks an outer one, so a bound-here Ctrl+A would eat the chord first). Drag-select still works; an app that wants keyboard select-all uses an explicit SelectionArea. |
scrollController: | ScrollController? | — | Optional scroll controller for the scrollable below us. When set, dragging near the top/bottom edge of the selectable region auto-scrolls the viewport and extends the selection into the newly-visible content — the standard “drag-to-extend past the edge” interaction of every native text editor. More |
autoScrollEdgeRows: | int | 1 | How many rows from the top/bottom edge of the selectable region trigger auto-scroll. Defaults to 1 — auto-scroll engages the instant the cursor enters the first or last visible row. |
autoScrollInterval: | Duration | const Duration(milliseconds: 50) | Interval between auto-scroll ticks while the cursor is in the edge zone. Defaults to 50ms (~20 rows per second). Used only when scrollController is set. |
child: | Widget | required | The subtree in which selection is enabled. |
focusNode: The supplied node’s focusability and traversal settings are preserved. Mounting the area never requests focus; selecting its text does.scrollController: When null, auto-scroll is disabled and dragging past the visible edge stops at the edge.
Source
Section titled “Source”SelectionArea is defined in packages/fleury/lib/src/widgets/selection/selection_area.dart.
Category: Input handling & focus · All widgets