SelectionArea class
Enables app-wide text selection in its subtree.
Wraps a subtree in:
- A SelectionScope that publishes a SelectionContainerDelegate, so descendant render objects (e.g. RenderText) can register themselves as Selectable.
- A GestureDetector that turns mouse drag into SelectionEdgeUpdateEvents.
- 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
-
- Object
- Widget
- StatefulWidget
- SelectionArea
- 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
plainTextis 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/curveapply to the whole chain. With notriggerit 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.