-
Notifications
You must be signed in to change notification settings - Fork 0
LayrzSelectionHandlePainter
A custom painter that renders text selection handles as teardrop-shaped glyphs.
Metadata
Domain: Selection
Phase: M2 (Selection framework)
Primitive: CustomPainter
Status: Shipped in 0.0.12.
LayrzSelectionHandlePainter is a CustomPainter that renders the teardrop-shaped selection handles used to mark the start, end, or collapsed position (caret) of a text selection.
The teardrop is a circular bulge with a square corner in one quadrant. The corner's orientation (which direction it points) depends on the handle type and is controlled by Transform.rotate in LayrzTextSelectionControls.
class LayrzSelectionHandlePainter extends CustomPainter {
/// The fill color of the handle.
///
/// Typically obtained from design tokens: [tokens.colors.primary].
final Color color;
const LayrzSelectionHandlePainter({
required this.color,
});
@override
void paint(Canvas canvas, Size size) {
// Renders the teardrop shape
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => ...;
}-
color (Color, required) — The fill color of the handle. Typically
tokens.colors.primary(brand color).
The handle is drawn as a teardrop: a circle combined with a square corner, producing a circular bulge with a pointed corner.
Unrotated, the teardrop's square corner points to the top-left (NW) quadrant of its 22×22 bounding box:
┌───────────────┐
│ ● │ ← corner at (0, 0)
│ ╱╱╱╱╱╱╱ │ (circular bulge)
│ ╱╱ circle ╱ │
│ ╱ containing ╱ │
│ ╱ square ╱ │
└───────────────┘
The painter combines:
- A circle (the bulge) centered at (11, 11) with radius 11
- A square (the corner) filling the top-left quadrant (0–11, 0–11)
The path's winding rule determines that the result is a teardrop: filled where both shapes overlap, creating a circular bulge with a square corner sticking out.
The handle's corner orientation is set via Transform.rotate in LayrzTextSelectionControls.buildHandle. Transform.rotate uses clockwise rotation for positive angles in Flutter:
| Handle Type | Rotation | Angle | Corner Points | Usage |
|---|---|---|---|---|
| left | 90° CW | π/2 | NE (up-right) | Selection start handle (left edge of selection) |
| right | 0° (no rotation) | 0 | NW (up-left) | Selection end handle (right edge of selection) |
| collapsed | 45° CW | π/4 | N (up) | Collapsed cursor (caret) |
Critical detail: These angles are exact and empirically determined. Changing them will misalign the handles with the text selection endpoints.
The painter fills the teardrop shape with a single Paint object using the specified color:
final paint = Paint()..color = color;
final path = Path()
..addOval(circle)
..addRect(point);
canvas.drawPath(path, paint);No stroke, no border, no additional visual effects — just a solid-filled teardrop.
The handle is always 22 × 22 logical pixels. This size is:
- Small enough to not obstruct the text being selected
- Large enough to be touch-friendly on mobile (recommended touch target ≥ 48×48, but the handle's tap region is enlarged via
GestureDetector) - Consistent with Material design conventions for selection handles
The painter is called with size = Size(22, 22) and scales its drawing to fit.
LayrzSelectionHandlePainter is not directly instantiated in normal use. Instead, it is created internally by LayrzTextSelectionControls.buildHandle:
CustomPaint(
size: Size(22, 22),
painter: LayrzSelectionHandlePainter(
color: tokens.colors.primary, // Brand color
),
)For custom selection implementations, you would create it like:
CustomPaint(
size: const Size(22, 22),
painter: LayrzSelectionHandlePainter(
color: Colors.blue, // Your chosen color
),
)The visual teardrop (22×22) is part of a larger interactive region:
- Visual size: 22 × 22 logical pixels (the teardrop)
-
Hit region: Defined by
SizedBox(width: 22, height: 22)withGestureDetector(behavior: HitTestBehavior.translucent), making the full bounding box tappable - Drag handling: Flutter's drag recognizers take over once the user taps and drags a handle to extend the selection
The painter is lightweight:
- No complex calculations or iterations
- Single path construction and fill operation
- No animation or state changes during paint (all state is external via
Transform)
Repainting is triggered only when the color changes, handled by shouldRepaint.
-
Path winding rule: The combination of
addOvalandaddRecton the same path uses the default even-odd winding rule, producing the teardrop shape where both shapes are filled -
Coordinate system: The painter works in the local 22×22 coordinate space.
Transform.rotateis applied outside the painter, rotating the entire rendered teardrop - Colorization: The handle color is typically the primary brand color for consistency, but any color can be supplied
-
No decoration: The handle is a pure shape with no shadow, border, or outline — styling is applied via
ContainerandBoxDecorationat a higher level if needed
- LayrzTextSelectionControls — uses this painter to render handles and controls rotation
- LayrzSelectionMagnifier — visual companion that appears during long-press+drag selection
- LayrzSelectionToolbar — toolbar shown after selection ends
Last updated: 2026-08-20
Related documents: LayrzTextSelectionControls, 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