Unverified Commit 09b6672e authored by xubaolin's avatar xubaolin Committed by GitHub

Do not crash when remove SelectableText during handle drag (#108369)

parent 16c25caa
......@@ -444,6 +444,9 @@ class TextSelectionOverlay {
late Offset _dragEndPosition;
void _handleSelectionEndHandleDragStart(DragStartDetails details) {
if (!renderObject.attached) {
return;
}
final Size handleSize = selectionControls!.getHandleSize(
renderObject.preferredLineHeight,
);
......@@ -451,6 +454,9 @@ class TextSelectionOverlay {
}
void _handleSelectionEndHandleDragUpdate(DragUpdateDetails details) {
if (!renderObject.attached) {
return;
}
_dragEndPosition += details.delta;
final TextPosition position = renderObject.getPositionForPoint(_dragEndPosition);
......@@ -492,6 +498,9 @@ class TextSelectionOverlay {
late Offset _dragStartPosition;
void _handleSelectionStartHandleDragStart(DragStartDetails details) {
if (!renderObject.attached) {
return;
}
final Size handleSize = selectionControls!.getHandleSize(
renderObject.preferredLineHeight,
);
......@@ -499,6 +508,9 @@ class TextSelectionOverlay {
}
void _handleSelectionStartHandleDragUpdate(DragUpdateDetails details) {
if (!renderObject.attached) {
return;
}
_dragStartPosition += details.delta;
final TextPosition position = renderObject.getPositionForPoint(_dragStartPosition);
......
......@@ -176,6 +176,74 @@ void main() {
);
}
testWidgets('Do not crash when remove SelectableText during handle drag', (WidgetTester tester) async {
// Regression test https://github.com/flutter/flutter/issues/108242
bool isShow = true;
late StateSetter setter;
await tester.pumpWidget(
MaterialApp(
home: Material(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
setter = setState;
if (isShow) {
return const SelectableText(
'abc def ghi',
dragStartBehavior: DragStartBehavior.down,
);
} else {
return const SizedBox.shrink();
}
},
),
),
),
);
final EditableText editableTextWidget = tester.widget(find.byType(EditableText));
final TextEditingController controller = editableTextWidget.controller;
// Long press the 'e' to select 'def'.
final Offset ePos = textOffsetToPosition(tester, 5);
TestGesture gesture = await tester.startGesture(ePos, pointer: 7);
await tester.pump(const Duration(seconds: 2));
await gesture.up();
await tester.pump();
await tester.pump(const Duration(milliseconds: 200)); // skip past the frame where the opacity is zero
final TextSelection selection = controller.selection;
expect(selection.baseOffset, 4);
expect(selection.extentOffset, 7);
final RenderEditable renderEditable = findRenderEditable(tester);
final List<TextSelectionPoint> endpoints = globalize(
renderEditable.getEndpointsForSelection(selection),
renderEditable,
);
expect(endpoints.length, 2);
// Drag the left handle to the left.
final Offset handlePos = endpoints[0].point + const Offset(-1.0, 1.0);
final Offset newHandlePos = textOffsetToPosition(tester, 1);
final Offset newHandlePos1 = textOffsetToPosition(tester, 0);
gesture = await tester.startGesture(handlePos, pointer: 7);
await tester.pump();
await gesture.moveTo(newHandlePos);
await tester.pump();
// Unmount the SelectableText during handle drag
setter(() {
isShow = false;
});
await tester.pump();
await gesture.moveTo(newHandlePos1);
await tester.pump(); // Do not crash here
await gesture.up();
await tester.pump();
});
testWidgets('has expected defaults', (WidgetTester tester) async {
await tester.pumpWidget(
boilerplate(
......
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