Skip to content

Commit 84cff31

Browse files
authored
Fix incorrect behavior of ScrollViewKeyboardDismissBehavior.onDrag for ScrollViewers with Drawer (#148948)
Fixes flutter/flutter#141542 Fixes flutter/flutter#103544 Fixes flutter/flutter#54277
1 parent de26ec8 commit 84cff31

6 files changed

+136
-9
lines changed

packages/flutter/lib/src/widgets/scroll_view.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -498,9 +498,9 @@ abstract class ScrollView extends StatelessWidget {
498498
return NotificationListener<ScrollUpdateNotification>(
499499
child: scrollableResult,
500500
onNotification: (ScrollUpdateNotification notification) {
501-
final FocusScopeNode focusScope = FocusScope.of(context);
502-
if (notification.dragDetails != null && focusScope.hasFocus) {
503-
focusScope.unfocus();
501+
final FocusScopeNode currentScope = FocusScope.of(context);
502+
if (notification.dragDetails != null && !currentScope.hasPrimaryFocus && currentScope.hasFocus) {
503+
FocusManager.instance.primaryFocus?.unfocus();
504504
}
505505
return false;
506506
},

packages/flutter/lib/src/widgets/single_child_scroll_view.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,9 @@ class SingleChildScrollView extends StatelessWidget {
270270
scrollable = NotificationListener<ScrollUpdateNotification>(
271271
child: scrollable,
272272
onNotification: (ScrollUpdateNotification notification) {
273-
final FocusScopeNode focusNode = FocusScope.of(context);
274-
if (notification.dragDetails != null && focusNode.hasFocus) {
275-
focusNode.unfocus();
273+
final FocusScopeNode currentScope = FocusScope.of(context);
274+
if (notification.dragDetails != null && !currentScope.hasPrimaryFocus && currentScope.hasFocus) {
275+
FocusManager.instance.primaryFocus?.unfocus();
276276
}
277277
return false;
278278
},

packages/flutter/lib/src/widgets/two_dimensional_scroll_view.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,9 @@ abstract class TwoDimensionalScrollView extends StatelessWidget {
190190
return NotificationListener<ScrollUpdateNotification>(
191191
child: scrollableResult,
192192
onNotification: (ScrollUpdateNotification notification) {
193-
final FocusScopeNode focusScope = FocusScope.of(context);
194-
if (notification.dragDetails != null && focusScope.hasFocus) {
195-
focusScope.unfocus();
193+
final FocusScopeNode currentScope = FocusScope.of(context);
194+
if (notification.dragDetails != null && !currentScope.hasPrimaryFocus && currentScope.hasFocus) {
195+
FocusManager.instance.primaryFocus?.unfocus();
196196
}
197197
return false;
198198
},

packages/flutter/test/widgets/scroll_view_test.dart

+41
Original file line numberDiff line numberDiff line change
@@ -1757,4 +1757,45 @@ void main() {
17571757
expect(item1Height, 30.0);
17581758
expect(item2Height, 30.0);
17591759
});
1760+
1761+
testWidgets('ListView dismiss keyboard onDrag and keep dismissed on drawer opened test', (WidgetTester tester) async {
1762+
final List<int> list = List<int>.generate(50, (int i) => i);
1763+
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
1764+
1765+
await tester.pumpWidget(textFieldBoilerplate(
1766+
child: Scaffold(
1767+
key: scaffoldKey,
1768+
drawer: Container(),
1769+
body: Column(
1770+
children: <Widget>[
1771+
const TextField(),
1772+
Expanded(
1773+
child: ListView(
1774+
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
1775+
children: list.map((int i) {
1776+
return Container(
1777+
height: 50,
1778+
);
1779+
}).toList(),
1780+
),
1781+
),
1782+
]
1783+
),
1784+
),
1785+
));
1786+
1787+
expect(tester.testTextInput.isVisible, isFalse);
1788+
final Finder finder = find.byType(TextField).first;
1789+
await tester.tap(finder);
1790+
expect(tester.testTextInput.isVisible, isTrue);
1791+
1792+
await tester.drag(find.byType(ListView).first, const Offset(0.0, -40.0));
1793+
await tester.pumpAndSettle();
1794+
1795+
expect(tester.testTextInput.isVisible, isFalse);
1796+
scaffoldKey.currentState!.openDrawer();
1797+
await tester.pumpAndSettle();
1798+
1799+
expect(tester.testTextInput.isVisible, isFalse);
1800+
});
17601801
}

packages/flutter/test/widgets/single_child_scroll_view_test.dart

+40
Original file line numberDiff line numberDiff line change
@@ -1078,4 +1078,44 @@ void main() {
10781078
await tester.pumpAndSettle();
10791079
expect(textField.focusNode!.hasFocus, isTrue);
10801080
});
1081+
1082+
testWidgets('keyboardDismissBehavior.OnDrag with drawer tests', (WidgetTester tester) async {
1083+
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
1084+
1085+
await tester.pumpWidget(
1086+
MaterialApp(
1087+
home: Scaffold(
1088+
key: scaffoldKey,
1089+
drawer: Container(),
1090+
body: Column(
1091+
children: <Widget>[
1092+
const TextField(),
1093+
Expanded(
1094+
child: SingleChildScrollView(
1095+
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
1096+
child: Container(
1097+
height: 1000,
1098+
)
1099+
),
1100+
),
1101+
]
1102+
),
1103+
),
1104+
),
1105+
);
1106+
1107+
expect(tester.testTextInput.isVisible, isFalse);
1108+
final Finder finder = find.byType(TextField).first;
1109+
await tester.tap(finder);
1110+
expect(tester.testTextInput.isVisible, isTrue);
1111+
1112+
await tester.drag(find.byType(SingleChildScrollView).first, const Offset(0.0, -40.0));
1113+
await tester.pumpAndSettle();
1114+
1115+
expect(tester.testTextInput.isVisible, isFalse);
1116+
scaffoldKey.currentState!.openDrawer();
1117+
await tester.pumpAndSettle();
1118+
1119+
expect(tester.testTextInput.isVisible, isFalse);
1120+
});
10811121
}

packages/flutter/test/widgets/two_dimensional_scroll_view_test.dart

+46
Original file line numberDiff line numberDiff line change
@@ -919,5 +919,51 @@ void main() {
919919
expect(horizontalController.position.pixels, 0.0);
920920
});
921921
});
922+
923+
testWidgets('Dismiss keyboard onDrag and keep dismissed on drawer opened', (WidgetTester tester) async {
924+
late final TwoDimensionalChildBuilderDelegate delegate;
925+
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
926+
addTearDown(() => delegate.dispose());
927+
928+
await tester.pumpWidget(
929+
MaterialApp(
930+
home: Scaffold(
931+
key: scaffoldKey,
932+
drawer: Container(),
933+
body: Column(
934+
children: <Widget>[
935+
const TextField(),
936+
Expanded(
937+
child: SimpleBuilderTableView(
938+
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
939+
delegate: delegate = TwoDimensionalChildBuilderDelegate(
940+
builder: _testChildBuilder,
941+
maxXIndex: 99,
942+
maxYIndex: 99,
943+
),
944+
),
945+
),
946+
]
947+
),
948+
),
949+
),
950+
);
951+
952+
await tester.pumpAndSettle();
953+
954+
expect(tester.testTextInput.isVisible, isFalse);
955+
final Finder finder = find.byType(TextField).first;
956+
await tester.tap(finder);
957+
expect(tester.testTextInput.isVisible, isTrue);
958+
959+
await tester.drag(find.byType(SimpleBuilderTableView).first, const Offset(-40.0, -40.0));
960+
await tester.pumpAndSettle();
961+
962+
expect(tester.testTextInput.isVisible, isFalse);
963+
scaffoldKey.currentState!.openDrawer();
964+
await tester.pumpAndSettle();
965+
966+
expect(tester.testTextInput.isVisible, isFalse);
967+
});
922968
});
923969
}

0 commit comments

Comments
 (0)