Unverified Commit 40b4bc99 authored by Justin McCandless's avatar Justin McCandless Committed by GitHub

Fix: Magnifier appears and won't dismiss (#128545)

Fixes a bug when tapping near certain TextFields.
parent 541fdd60
...@@ -1088,10 +1088,26 @@ class SelectionOverlay { ...@@ -1088,10 +1088,26 @@ class SelectionOverlay {
void _handleStartHandleDragStart(DragStartDetails details) { void _handleStartHandleDragStart(DragStartDetails details) {
assert(!_isDraggingStartHandle); assert(!_isDraggingStartHandle);
// Calling OverlayEntry.remove may not happen until the following frame, so
// it's possible for the handles to receive a gesture after calling remove.
if (_handles == null) {
_isDraggingStartHandle = false;
return;
}
_isDraggingStartHandle = details.kind == PointerDeviceKind.touch; _isDraggingStartHandle = details.kind == PointerDeviceKind.touch;
onStartHandleDragStart?.call(details); onStartHandleDragStart?.call(details);
} }
void _handleStartHandleDragUpdate(DragUpdateDetails details) {
// Calling OverlayEntry.remove may not happen until the following frame, so
// it's possible for the handles to receive a gesture after calling remove.
if (_handles == null) {
_isDraggingStartHandle = false;
return;
}
onStartHandleDragUpdate?.call(details);
}
/// Called when the users drag the start selection handles to new locations. /// Called when the users drag the start selection handles to new locations.
final ValueChanged<DragUpdateDetails>? onStartHandleDragUpdate; final ValueChanged<DragUpdateDetails>? onStartHandleDragUpdate;
...@@ -1101,6 +1117,11 @@ class SelectionOverlay { ...@@ -1101,6 +1117,11 @@ class SelectionOverlay {
void _handleStartHandleDragEnd(DragEndDetails details) { void _handleStartHandleDragEnd(DragEndDetails details) {
_isDraggingStartHandle = false; _isDraggingStartHandle = false;
// Calling OverlayEntry.remove may not happen until the following frame, so
// it's possible for the handles to receive a gesture after calling remove.
if (_handles == null) {
return;
}
onStartHandleDragEnd?.call(details); onStartHandleDragEnd?.call(details);
} }
...@@ -1147,10 +1168,26 @@ class SelectionOverlay { ...@@ -1147,10 +1168,26 @@ class SelectionOverlay {
void _handleEndHandleDragStart(DragStartDetails details) { void _handleEndHandleDragStart(DragStartDetails details) {
assert(!_isDraggingEndHandle); assert(!_isDraggingEndHandle);
// Calling OverlayEntry.remove may not happen until the following frame, so
// it's possible for the handles to receive a gesture after calling remove.
if (_handles == null) {
_isDraggingEndHandle = false;
return;
}
_isDraggingEndHandle = details.kind == PointerDeviceKind.touch; _isDraggingEndHandle = details.kind == PointerDeviceKind.touch;
onEndHandleDragStart?.call(details); onEndHandleDragStart?.call(details);
} }
void _handleEndHandleDragUpdate(DragUpdateDetails details) {
// Calling OverlayEntry.remove may not happen until the following frame, so
// it's possible for the handles to receive a gesture after calling remove.
if (_handles == null) {
_isDraggingEndHandle = false;
return;
}
onEndHandleDragUpdate?.call(details);
}
/// Called when the users drag the end selection handles to new locations. /// Called when the users drag the end selection handles to new locations.
final ValueChanged<DragUpdateDetails>? onEndHandleDragUpdate; final ValueChanged<DragUpdateDetails>? onEndHandleDragUpdate;
...@@ -1160,6 +1197,11 @@ class SelectionOverlay { ...@@ -1160,6 +1197,11 @@ class SelectionOverlay {
void _handleEndHandleDragEnd(DragEndDetails details) { void _handleEndHandleDragEnd(DragEndDetails details) {
_isDraggingEndHandle = false; _isDraggingEndHandle = false;
// Calling OverlayEntry.remove may not happen until the following frame, so
// it's possible for the handles to receive a gesture after calling remove.
if (_handles == null) {
return;
}
onEndHandleDragEnd?.call(details); onEndHandleDragEnd?.call(details);
} }
...@@ -1472,7 +1514,7 @@ class SelectionOverlay { ...@@ -1472,7 +1514,7 @@ class SelectionOverlay {
handleLayerLink: startHandleLayerLink, handleLayerLink: startHandleLayerLink,
onSelectionHandleTapped: onSelectionHandleTapped, onSelectionHandleTapped: onSelectionHandleTapped,
onSelectionHandleDragStart: _handleStartHandleDragStart, onSelectionHandleDragStart: _handleStartHandleDragStart,
onSelectionHandleDragUpdate: onStartHandleDragUpdate, onSelectionHandleDragUpdate: _handleStartHandleDragUpdate,
onSelectionHandleDragEnd: _handleStartHandleDragEnd, onSelectionHandleDragEnd: _handleStartHandleDragEnd,
selectionControls: selectionControls, selectionControls: selectionControls,
visibility: startHandlesVisible, visibility: startHandlesVisible,
...@@ -1499,7 +1541,7 @@ class SelectionOverlay { ...@@ -1499,7 +1541,7 @@ class SelectionOverlay {
handleLayerLink: endHandleLayerLink, handleLayerLink: endHandleLayerLink,
onSelectionHandleTapped: onSelectionHandleTapped, onSelectionHandleTapped: onSelectionHandleTapped,
onSelectionHandleDragStart: _handleEndHandleDragStart, onSelectionHandleDragStart: _handleEndHandleDragStart,
onSelectionHandleDragUpdate: onEndHandleDragUpdate, onSelectionHandleDragUpdate: _handleEndHandleDragUpdate,
onSelectionHandleDragEnd: _handleEndHandleDragEnd, onSelectionHandleDragEnd: _handleEndHandleDragEnd,
selectionControls: selectionControls, selectionControls: selectionControls,
visibility: endHandlesVisible, visibility: endHandlesVisible,
...@@ -1752,7 +1794,7 @@ class _SelectionHandleOverlayState extends State<_SelectionHandleOverlay> with S ...@@ -1752,7 +1794,7 @@ class _SelectionHandleOverlayState extends State<_SelectionHandleOverlay> with S
// Make sure the GestureDetector is big enough to be easily interactive. // Make sure the GestureDetector is big enough to be easily interactive.
final Rect interactiveRect = handleRect.expandToInclude( final Rect interactiveRect = handleRect.expandToInclude(
Rect.fromCircle(center: handleRect.center, radius: kMinInteractiveDimension/ 2), Rect.fromCircle(center: handleRect.center, radius: kMinInteractiveDimension / 2),
); );
final RelativeRect padding = RelativeRect.fromLTRB( final RelativeRect padding = RelativeRect.fromLTRB(
math.max((interactiveRect.width - handleRect.width) / 2, 0), math.max((interactiveRect.width - handleRect.width) / 2, 0),
......
...@@ -15803,7 +15803,7 @@ void main() { ...@@ -15803,7 +15803,7 @@ void main() {
); );
final TextField textField = TextField( final TextField textField = TextField(
magnifierConfiguration: TextMagnifierConfiguration( magnifierConfiguration: TextMagnifierConfiguration(
magnifierBuilder: (_, __, ___) => customMagnifier, magnifierBuilder: (BuildContext context, MagnifierController controller, ValueNotifier<MagnifierInfo>? info) => customMagnifier,
), ),
); );
...@@ -15901,7 +15901,7 @@ void main() { ...@@ -15901,7 +15901,7 @@ void main() {
controller: controller, controller: controller,
magnifierConfiguration: TextMagnifierConfiguration( magnifierConfiguration: TextMagnifierConfiguration(
magnifierBuilder: ( magnifierBuilder: (
_, BuildContext context,
MagnifierController controller, MagnifierController controller,
ValueNotifier<MagnifierInfo> localMagnifierInfo ValueNotifier<MagnifierInfo> localMagnifierInfo
) { ) {
...@@ -15965,7 +15965,7 @@ void main() { ...@@ -15965,7 +15965,7 @@ void main() {
controller: controller, controller: controller,
magnifierConfiguration: TextMagnifierConfiguration( magnifierConfiguration: TextMagnifierConfiguration(
magnifierBuilder: ( magnifierBuilder: (
_, BuildContext context,
MagnifierController controller, MagnifierController controller,
ValueNotifier<MagnifierInfo> localMagnifierInfo ValueNotifier<MagnifierInfo> localMagnifierInfo
) { ) {
...@@ -16067,7 +16067,7 @@ void main() { ...@@ -16067,7 +16067,7 @@ void main() {
controller: controller, controller: controller,
magnifierConfiguration: TextMagnifierConfiguration( magnifierConfiguration: TextMagnifierConfiguration(
magnifierBuilder: ( magnifierBuilder: (
_, BuildContext context,
MagnifierController controller, MagnifierController controller,
ValueNotifier<MagnifierInfo> localMagnifierInfo ValueNotifier<MagnifierInfo> localMagnifierInfo
) { ) {
...@@ -16118,6 +16118,54 @@ void main() { ...@@ -16118,6 +16118,54 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.byKey(fakeMagnifier.key!), findsNothing); expect(find.byKey(fakeMagnifier.key!), findsNothing);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.iOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.iOS }));
testWidgets('magnifier does not show when tapping outside field', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/128321
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Padding(
padding: const EdgeInsets.all(20),
child: TextField(
magnifierConfiguration: TextMagnifierConfiguration(
magnifierBuilder: (
BuildContext context,
MagnifierController controller,
ValueNotifier<MagnifierInfo> localMagnifierInfo
) {
magnifierInfo = localMagnifierInfo;
return fakeMagnifier;
},
),
onTapOutside: (PointerDownEvent event) {
FocusManager.instance.primaryFocus?.unfocus();
}
),
),
),
),
);
await tester.tapAt(
tester.getCenter(find.byType(TextField)),
);
await tester.pump();
expect(find.byKey(fakeMagnifier.key!), findsNothing);
final TestGesture gesture = await tester.startGesture(
tester.getBottomLeft(find.byType(TextField)) - const Offset(10.0, 20.0),
);
await tester.pump();
expect(find.byKey(fakeMagnifier.key!), findsNothing);
await gesture.up();
await tester.pump();
expect(find.byKey(fakeMagnifier.key!), findsNothing);
},
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android }),
);
}); });
group('TapRegion integration', () { group('TapRegion integration', () {
...@@ -16513,7 +16561,7 @@ class _ObscureTextTestWidgetState extends State<_ObscureTextTestWidget> { ...@@ -16513,7 +16561,7 @@ class _ObscureTextTestWidgetState extends State<_ObscureTextTestWidget> {
return MaterialApp( return MaterialApp(
home: Scaffold( home: Scaffold(
body: Builder( body: Builder(
builder: (_) { builder: (BuildContext context) {
return Column( return Column(
children: <Widget>[ children: <Widget>[
TextField( TextField(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment