-
Notifications
You must be signed in to change notification settings - Fork 0
LayrzTextSelectionControls
A Material-free text selection controls implementation providing selection handles and context menu integration for LayrzTextInput and other editable text fields.
Metadata
Domain: Selection
Phase: M2 (Selection framework)
Primitive: TextSelectionControls, TextSelectionHandleControls
Status: Shipped in 0.0.12.
LayrzTextSelectionControls implements the Flutter TextSelectionControls interface to provide styled selection handles (for touch drag-to-select) and a Material-free selection toolbar integration. It is a singleton to prevent the EditableText widget from recreating the selection overlay on every rebuild.
EditableText.didUpdateWidget disposes and recreates the selection overlay whenever the selectionControls parameter differs between builds. If a new instance is passed on every rebuild, the overlay flickers and the selection is lost. By using a singleton instance, this is avoided: all EditableText widgets in the app share the same controls, and the overlay survives rebuilds.
Tokens are accessed from BuildContext at render time, so theme changes are reflected automatically without recreating the instance.
class LayrzTextSelectionControls extends TextSelectionControls
with TextSelectionHandleControls {
/// Returns the singleton instance of [LayrzTextSelectionControls].
static LayrzTextSelectionControls get instance => _instance;
/// Creates an instance (private; use [instance] getter instead).
LayrzTextSelectionControls._internal();
}Assign the singleton to an editable field's selectionControls parameter:
EditableText(
controller: myController,
focusNode: myFocusNode,
style: myTextStyle,
cursorColor: Colors.blue,
backgroundCursorColor: Colors.grey,
selectionControls: LayrzTextSelectionControls.instance, // ← Use singleton
// ... other parameters
)For LayrzTextInput, this is already integrated — no additional configuration needed.
The controls render selection handles as teardrop-shaped glyphs that mark the start and end positions of a text selection.
-
Color:
tokens.colors.primary(brand color) - Size: 22 × 22 logical pixels
- Shape: Teardrop (circle with a square corner in one quadrant)
-
Corner orientation: Rotated to point toward the selected text
- Left handle (selection start): rotated 90° clockwise → corner points up-right (NE)
- Right handle (selection end): no rotation → corner points up-left (NW)
- Collapsed cursor (single caret): rotated 45° clockwise → corner points up (N)
The unrotated teardrop corner is at the top-left (NW). Transform.rotate uses clockwise rotation for positive angles in Flutter. This mapping is exact:
// left: π/2 radians = 90° → corner at NE
Transform.rotate(angle: math.pi / 2.0, child: handle)
// right: 0 radians = 0° → corner at NW (unrotated)
handle
// collapsed: π/4 radians = 45° → corner at N
Transform.rotate(angle: math.pi / 4.0, child: handle)This exact geometry was determined during testing and is not arbitrary. Changing these angles will misalign the handles with the text selection endpoints.
- Single tap: Places the caret at the tapped position (collapsed cursor)
- Tap on a handle: Allows dragging the handle to resize the selection
- Long-press + drag: Selects text by dragging a handle; magnifier appears on touch devices (see LayrzSelectionMagnifier)
The controls integrate with Flutter's TextSelectionHandleControls mixin, which means the toolbar is shown via EditableText.contextMenuBuilder rather than the deprecated buildToolbar method.
The toolbar displays actions from LayrzSelectableAction:
- Built-in actions: Copy, Cut, Paste, Select All (shown as permitted by field state)
- Custom actions: application-specific operations (e.g., "Translate", "Share")
The toolbar is rendered by LayrzSelectionToolbar, which applies the dark overlay surface treatment (fg1 background with sf1 content).
The singleton overrides operator== and hashCode for identity equality:
@override
bool operator ==(Object other) {
return identical(this, other) || other is LayrzTextSelectionControls;
}
@override
int get hashCode => 0; // Constant hash for singletonThis ensures that comparing any LayrzTextSelectionControls instance to any other always returns true (because all are the same singleton), and the hash is constant.
- Singleton stability: The one instance persists for the app's lifetime. Creating it multiple times or recreating it on rebuild will break overlay stability.
-
Theme integration: All visual properties (colors, sizes, spacing) are derived from
context.tokens, allowing theme changes to be reflected at render time without recreating the instance. - Handle anchor calculation: The anchor point determines where the teardrop corner attaches to the text. For each handle type, the anchor is positioned precisely to align the corner with the selection edge, accounting for the 22×22 bounding box.
- Accessibility: Selection handles are announced to screen readers; dragging is reported as a selection extension operation.
- LayrzSelectionToolbar — renders the action button bar above/below the selection
- LayrzSelectableAction — defines the actions displayed in the toolbar
- LayrzSelectionMagnifier — magnifier widget shown during long-press+drag on touch
- LayrzSelectionHandlePainter — custom painter that renders the teardrop glyph
Last updated: 2026-08-20
Related documents: LayrzTextInput, LayrzSelectionToolbar, Selection (D50)
Made with ❤️ by Golden M, Inc.
- LayrzTextInput
- LayrzSelectableAction
- LayrzSelectionToolbar
- LayrzTextSelectionControls
- LayrzSelectionMagnifier
- LayrzSelectionHandlePainter
- LayrzTextAreaInput
- LayrzComboBoxInput
- LayrzNumberInput
- LayrzPasswordInput
- LayrzCheckboxInput
- LayrzRadioInput
- LayrzSelectInput
- LayrzMultiSelectInput
- LayrzSearchInput
- LayrzDualListInput
- LayrzDurationInput