SelectionArea class

Enables app-wide text selection in its subtree.

Wraps a subtree in:

  1. A SelectionScope that publishes a SelectionContainerDelegate, so descendant render objects (e.g. RenderText) can register themselves as Selectable.
  2. A GestureDetector that turns mouse drag into SelectionEdgeUpdateEvents.
  3. A KeyBindings block providing Ctrl+A (select all), Ctrl+C (copy via the ambient ClipboardScope), 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.

SelectionArea(
  copyOnRelease: true,
  onSelectionChanged: (sel) => statusBar.text = sel?.plainText ?? '',
  child: Column(children: [
    Text('First paragraph.'),
    Text('Second paragraph.'),
  ]),
)

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.

Inheritance
Available extensions

Constructors

SelectionArea({Key? key, FocusNode? focusNode, SelectionChangedCallback? onSelectionChanged, bool copyOnRelease = false, bool copyClearsSelection = false, bool selectAllShortcut = true, ScrollController? scrollController, int autoScrollEdgeRows = 1, Duration autoScrollInterval = const Duration(milliseconds: 50), required Widget child})
const

Properties

autoScrollEdgeRows → int
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.
final
autoScrollInterval → Duration
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.
final
child → Widget
The subtree in which selection is enabled.
final
copyClearsSelection → bool
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.
final
copyOnRelease → bool
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.
final
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.
final
hashCode → int
The hash code for this object.
no setterinherited
key → Key?
finalinherited
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.
final
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
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.
final
selectAllShortcut → bool
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.
final

Methods

animate({Duration? duration, Curve? curve, Object? trigger, bool repeat = false}) → Animate

Available on Widget, provided by the AnimateExtension extension

Starts an Animate chain over this widget. duration / curve apply to the whole chain. With no trigger it plays on mount; with a non-null trigger it rests on mount and replays whenever the token changes.
createElement() → StatefulElement
Creates the Element that will mount this widget into the tree.
inherited
createState() → State<SelectionArea>
Creates the mutable state for this widget at the current location in the tree. Called exactly once per StatefulElement instance.
override
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toString() → String
A string representation of this object.
inherited

Operators

operator ==(Object other) → bool
The equality operator.
inherited

Static Methods

disabled({Key? key, required Widget child}) → Widget
Wraps a subtree that should NOT participate in any ancestor SelectionArea's selection — Selectables here see no ambient registrar and silently no-op. Useful for forms, modal dialogs, or interactive panels embedded inside a selectable region.