@@ -515,6 +515,11 @@ class TextSelectionOverlay {
515515 }
516516 _value = newValue;
517517 _updateSelectionOverlay ();
518+ // _updateSelectionOverlay may not rebuild the selection overlay if the
519+ // text metrics and selection doesn't change even if the text has changed.
520+ // This rebuild is needed for the toolbar to update based on the latest text
521+ // value.
522+ _selectionOverlay.markNeedsBuild ();
518523 }
519524
520525 void _updateSelectionOverlay () {
@@ -541,7 +546,13 @@ class TextSelectionOverlay {
541546 ///
542547 /// This is intended to be called when the [renderObject] may have changed its
543548 /// text metrics (e.g. because the text was scrolled).
544- void updateForScroll () => _updateSelectionOverlay ();
549+ void updateForScroll () {
550+ _updateSelectionOverlay ();
551+ // This method may be called due to windows metrics changes. In that case,
552+ // non of the properties in _selectionOverlay will change, but a rebuild is
553+ // still needed.
554+ _selectionOverlay.markNeedsBuild ();
555+ }
545556
546557 /// Whether the handles are currently visible.
547558 bool get handlesAreVisible => _selectionOverlay._handles != null && handlesVisible;
@@ -1030,7 +1041,7 @@ class SelectionOverlay {
10301041 return ;
10311042 }
10321043 _startHandleType = value;
1033- _markNeedsBuild ();
1044+ markNeedsBuild ();
10341045 }
10351046
10361047 /// The line height at the selection start.
@@ -1045,9 +1056,11 @@ class SelectionOverlay {
10451056 return ;
10461057 }
10471058 _lineHeightAtStart = value;
1048- _markNeedsBuild ();
1059+ markNeedsBuild ();
10491060 }
10501061
1062+ bool _isDraggingStartHandle = false ;
1063+
10511064 /// Whether the start handle is visible.
10521065 ///
10531066 /// If the value changes, the start handle uses [FadeTransition] to transition
@@ -1059,13 +1072,24 @@ class SelectionOverlay {
10591072 /// Called when the users start dragging the start selection handles.
10601073 final ValueChanged <DragStartDetails >? onStartHandleDragStart;
10611074
1075+ void _handleStartHandleDragStart (DragStartDetails details) {
1076+ assert (! _isDraggingStartHandle);
1077+ _isDraggingStartHandle = details.kind == PointerDeviceKind .touch;
1078+ onStartHandleDragStart? .call (details);
1079+ }
1080+
10621081 /// Called when the users drag the start selection handles to new locations.
10631082 final ValueChanged <DragUpdateDetails >? onStartHandleDragUpdate;
10641083
10651084 /// Called when the users lift their fingers after dragging the start selection
10661085 /// handles.
10671086 final ValueChanged <DragEndDetails >? onStartHandleDragEnd;
10681087
1088+ void _handleStartHandleDragEnd (DragEndDetails details) {
1089+ _isDraggingStartHandle = false ;
1090+ onStartHandleDragEnd? .call (details);
1091+ }
1092+
10691093 /// The type of end selection handle.
10701094 ///
10711095 /// Changing the value while the handles are visible causes them to rebuild.
@@ -1076,7 +1100,7 @@ class SelectionOverlay {
10761100 return ;
10771101 }
10781102 _endHandleType = value;
1079- _markNeedsBuild ();
1103+ markNeedsBuild ();
10801104 }
10811105
10821106 /// The line height at the selection end.
@@ -1091,9 +1115,11 @@ class SelectionOverlay {
10911115 return ;
10921116 }
10931117 _lineHeightAtEnd = value;
1094- _markNeedsBuild ();
1118+ markNeedsBuild ();
10951119 }
10961120
1121+ bool _isDraggingEndHandle = false ;
1122+
10971123 /// Whether the end handle is visible.
10981124 ///
10991125 /// If the value changes, the end handle uses [FadeTransition] to transition
@@ -1105,13 +1131,24 @@ class SelectionOverlay {
11051131 /// Called when the users start dragging the end selection handles.
11061132 final ValueChanged <DragStartDetails >? onEndHandleDragStart;
11071133
1134+ void _handleEndHandleDragStart (DragStartDetails details) {
1135+ assert (! _isDraggingEndHandle);
1136+ _isDraggingEndHandle = details.kind == PointerDeviceKind .touch;
1137+ onEndHandleDragStart? .call (details);
1138+ }
1139+
11081140 /// Called when the users drag the end selection handles to new locations.
11091141 final ValueChanged <DragUpdateDetails >? onEndHandleDragUpdate;
11101142
11111143 /// Called when the users lift their fingers after dragging the end selection
11121144 /// handles.
11131145 final ValueChanged <DragEndDetails >? onEndHandleDragEnd;
11141146
1147+ void _handleEndHandleDragEnd (DragEndDetails details) {
1148+ _isDraggingEndHandle = false ;
1149+ onEndHandleDragEnd? .call (details);
1150+ }
1151+
11151152 /// Whether the toolbar is visible.
11161153 ///
11171154 /// If the value changes, the toolbar uses [FadeTransition] to transition
@@ -1125,7 +1162,21 @@ class SelectionOverlay {
11251162 List <TextSelectionPoint > _selectionEndpoints;
11261163 set selectionEndpoints (List <TextSelectionPoint > value) {
11271164 if (! listEquals (_selectionEndpoints, value)) {
1128- _markNeedsBuild ();
1165+ markNeedsBuild ();
1166+ if ((_isDraggingEndHandle || _isDraggingStartHandle) &&
1167+ _startHandleType != TextSelectionHandleType .collapsed) {
1168+ switch (defaultTargetPlatform) {
1169+ case TargetPlatform .android:
1170+ HapticFeedback .selectionClick ();
1171+ break ;
1172+ case TargetPlatform .fuchsia:
1173+ case TargetPlatform .iOS:
1174+ case TargetPlatform .linux:
1175+ case TargetPlatform .macOS:
1176+ case TargetPlatform .windows:
1177+ break ;
1178+ }
1179+ }
11291180 }
11301181 _selectionEndpoints = value;
11311182 }
@@ -1220,7 +1271,7 @@ class SelectionOverlay {
12201271 return ;
12211272 }
12221273 _toolbarLocation = value;
1223- _markNeedsBuild ();
1274+ markNeedsBuild ();
12241275 }
12251276
12261277 /// Controls the fade-in and fade-out animations for the toolbar and handles.
@@ -1250,7 +1301,6 @@ class SelectionOverlay {
12501301 OverlayEntry (builder: _buildStartHandle),
12511302 OverlayEntry (builder: _buildEndHandle),
12521303 ];
1253-
12541304 Overlay .of (context, rootOverlay: true , debugRequiredFor: debugRequiredFor).insertAll (_handles! );
12551305 }
12561306
@@ -1299,7 +1349,9 @@ class SelectionOverlay {
12991349 }
13001350
13011351 bool _buildScheduled = false ;
1302- void _markNeedsBuild () {
1352+
1353+ /// Rebuilds the selection toolbar or handles if they are present.
1354+ void markNeedsBuild () {
13031355 if (_handles == null && _toolbar == null ) {
13041356 return ;
13051357 }
@@ -1379,9 +1431,9 @@ class SelectionOverlay {
13791431 type: _startHandleType,
13801432 handleLayerLink: startHandleLayerLink,
13811433 onSelectionHandleTapped: onSelectionHandleTapped,
1382- onSelectionHandleDragStart: onStartHandleDragStart ,
1434+ onSelectionHandleDragStart: _handleStartHandleDragStart ,
13831435 onSelectionHandleDragUpdate: onStartHandleDragUpdate,
1384- onSelectionHandleDragEnd: onStartHandleDragEnd ,
1436+ onSelectionHandleDragEnd: _handleStartHandleDragEnd ,
13851437 selectionControls: selectionControls,
13861438 visibility: startHandlesVisible,
13871439 preferredLineHeight: _lineHeightAtStart,
@@ -1406,9 +1458,9 @@ class SelectionOverlay {
14061458 type: _endHandleType,
14071459 handleLayerLink: endHandleLayerLink,
14081460 onSelectionHandleTapped: onSelectionHandleTapped,
1409- onSelectionHandleDragStart: onEndHandleDragStart ,
1461+ onSelectionHandleDragStart: _handleEndHandleDragStart ,
14101462 onSelectionHandleDragUpdate: onEndHandleDragUpdate,
1411- onSelectionHandleDragEnd: onEndHandleDragEnd ,
1463+ onSelectionHandleDragEnd: _handleEndHandleDragEnd ,
14121464 selectionControls: selectionControls,
14131465 visibility: endHandlesVisible,
14141466 preferredLineHeight: _lineHeightAtEnd,
0 commit comments