Skip to content

Commit 5517cc9

Browse files
EArminjonPiinks
andauthored
feat: Change default value of keyboardDismissBehavior (flutter#158580)
This PR aim to let developer choose the default ScrollViewKeyboardDismissBehavior value. I removed all default values of keyboardDismissBehavior and instead use the one from `ScrollConfiguration`, which use as default value `ScrollViewKeyboardDismissBehavior.manual`. This PR try to fix : flutter#158566 <details/> <summary> Code Example </summary> ```dart import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @OverRide Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', scrollBehavior: const MaterialScrollBehavior().copyWith( keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag, ), theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @OverRide State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @OverRide Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: TextField(), ), body: ListView.builder( itemCount: 100, itemBuilder: (context, index) => ListTile(title: Text('Item $index')), ), ); } } ``` </details> https://github.com/user-attachments/assets/8341c3da-2685-4f55-b8e9-11d2aae907db ## Pre-launch Checklist - [X] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [X] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [X] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [X] I signed the [CLA]. - [X] I listed at least one issue that this PR fixes in the description above. - [X] I updated/added relevant documentation (doc comments with `///`). - [X] I added new tests to check the change I am making, or this PR is [test-exempt]. - [X] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [X] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Kate Lovett <katelovett@google.com>
1 parent 36eb78b commit 5517cc9

File tree

8 files changed

+152
-17
lines changed

8 files changed

+152
-17
lines changed

packages/flutter/lib/src/material/reorderable_list.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class ReorderableListView extends StatefulWidget {
9898
this.anchor = 0.0,
9999
this.cacheExtent,
100100
this.dragStartBehavior = DragStartBehavior.start,
101-
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
101+
this.keyboardDismissBehavior,
102102
this.restorationId,
103103
this.clipBehavior = Clip.hardEdge,
104104
this.autoScrollerVelocityScalar,
@@ -169,7 +169,7 @@ class ReorderableListView extends StatefulWidget {
169169
this.anchor = 0.0,
170170
this.cacheExtent,
171171
this.dragStartBehavior = DragStartBehavior.start,
172-
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
172+
this.keyboardDismissBehavior,
173173
this.restorationId,
174174
this.clipBehavior = Clip.hardEdge,
175175
this.autoScrollerVelocityScalar,
@@ -271,8 +271,9 @@ class ReorderableListView extends StatefulWidget {
271271

272272
/// {@macro flutter.widgets.scroll_view.keyboardDismissBehavior}
273273
///
274-
/// The default is [ScrollViewKeyboardDismissBehavior.manual]
275-
final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
274+
/// If [keyboardDismissBehavior] is null then it will fallback to the inherited
275+
/// [ScrollBehavior.getKeyboardDismissBehavior].
276+
final ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
276277

277278
/// {@macro flutter.widgets.scrollable.restorationId}
278279
final String? restorationId;

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ class ReorderableList extends StatefulWidget {
168168
this.anchor = 0.0,
169169
this.cacheExtent,
170170
this.dragStartBehavior = DragStartBehavior.start,
171-
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
171+
this.keyboardDismissBehavior,
172172
this.restorationId,
173173
this.clipBehavior = Clip.hardEdge,
174174
this.autoScrollerVelocityScalar,
@@ -280,8 +280,9 @@ class ReorderableList extends StatefulWidget {
280280

281281
/// {@macro flutter.widgets.scroll_view.keyboardDismissBehavior}
282282
///
283-
/// The default is [ScrollViewKeyboardDismissBehavior.manual]
284-
final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
283+
/// If [keyboardDismissBehavior] is null then it will fallback to the inherited
284+
/// [ScrollBehavior.getKeyboardDismissBehavior].
285+
final ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
285286

286287
/// {@macro flutter.widgets.scrollable.restorationId}
287288
final String? restorationId;

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import 'package:flutter/services.dart' show LogicalKeyboardKey;
1818
import 'framework.dart';
1919
import 'overscroll_indicator.dart';
2020
import 'scroll_physics.dart';
21+
import 'scroll_view.dart';
2122
import 'scrollable.dart';
2223
import 'scrollable_helpers.dart';
2324
import 'scrollbar.dart';
@@ -89,6 +90,7 @@ class ScrollBehavior {
8990
Set<LogicalKeyboardKey>? pointerAxisModifiers,
9091
ScrollPhysics? physics,
9192
TargetPlatform? platform,
93+
ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior,
9294
}) {
9395
return _WrappedScrollBehavior(
9496
delegate: this,
@@ -99,6 +101,7 @@ class ScrollBehavior {
99101
pointerAxisModifiers: pointerAxisModifiers,
100102
physics: physics,
101103
platform: platform,
104+
keyboardDismissBehavior: keyboardDismissBehavior,
102105
);
103106
}
104107

@@ -264,6 +267,12 @@ class ScrollBehavior {
264267
/// method returns false, the rebuilds might be optimized away.
265268
bool shouldNotify(covariant ScrollBehavior oldDelegate) => false;
266269

270+
/// The default keyboard dismissal behavior for [ScrollView] widgets.
271+
///
272+
/// Defaults to [ScrollViewKeyboardDismissBehavior.manual].
273+
ScrollViewKeyboardDismissBehavior getKeyboardDismissBehavior(BuildContext context) =>
274+
ScrollViewKeyboardDismissBehavior.manual;
275+
267276
@override
268277
String toString() => objectRuntimeType(this, 'ScrollBehavior');
269278
}
@@ -278,6 +287,7 @@ class _WrappedScrollBehavior implements ScrollBehavior {
278287
Set<LogicalKeyboardKey>? pointerAxisModifiers,
279288
this.physics,
280289
this.platform,
290+
this.keyboardDismissBehavior,
281291
}) : _dragDevices = dragDevices,
282292
_pointerAxisModifiers = pointerAxisModifiers;
283293

@@ -286,6 +296,7 @@ class _WrappedScrollBehavior implements ScrollBehavior {
286296
final bool overscroll;
287297
final ScrollPhysics? physics;
288298
final TargetPlatform? platform;
299+
final ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
289300
final Set<PointerDeviceKind>? _dragDevices;
290301
final MultitouchDragStrategy? multitouchDragStrategy;
291302
final Set<LogicalKeyboardKey>? _pointerAxisModifiers;
@@ -327,6 +338,7 @@ class _WrappedScrollBehavior implements ScrollBehavior {
327338
Set<LogicalKeyboardKey>? pointerAxisModifiers,
328339
ScrollPhysics? physics,
329340
TargetPlatform? platform,
341+
ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior,
330342
}) {
331343
return delegate.copyWith(
332344
scrollbars: scrollbars ?? this.scrollbars,
@@ -336,6 +348,7 @@ class _WrappedScrollBehavior implements ScrollBehavior {
336348
pointerAxisModifiers: pointerAxisModifiers ?? this.pointerAxisModifiers,
337349
physics: physics ?? this.physics,
338350
platform: platform ?? this.platform,
351+
keyboardDismissBehavior: keyboardDismissBehavior ?? this.keyboardDismissBehavior,
339352
);
340353
}
341354

@@ -349,6 +362,11 @@ class _WrappedScrollBehavior implements ScrollBehavior {
349362
return physics ?? delegate.getScrollPhysics(context);
350363
}
351364

365+
@override
366+
ScrollViewKeyboardDismissBehavior getKeyboardDismissBehavior(BuildContext context) {
367+
return keyboardDismissBehavior ?? delegate.getKeyboardDismissBehavior(context);
368+
}
369+
352370
@override
353371
bool shouldNotify(_WrappedScrollBehavior oldDelegate) {
354372
return oldDelegate.delegate.runtimeType != delegate.runtimeType ||

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ abstract class ScrollView extends StatelessWidget {
118118
this.cacheExtent,
119119
this.semanticChildCount,
120120
this.dragStartBehavior = DragStartBehavior.start,
121-
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
121+
this.keyboardDismissBehavior,
122122
this.restorationId,
123123
this.clipBehavior = Clip.hardEdge,
124124
this.hitTestBehavior = HitTestBehavior.opaque,
@@ -374,10 +374,14 @@ abstract class ScrollView extends StatelessWidget {
374374
final DragStartBehavior dragStartBehavior;
375375

376376
/// {@template flutter.widgets.scroll_view.keyboardDismissBehavior}
377-
/// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will
377+
/// The [ScrollViewKeyboardDismissBehavior] defines how this [ScrollView] will
378378
/// dismiss the keyboard automatically.
379379
/// {@endtemplate}
380-
final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
380+
///
381+
/// If [keyboardDismissBehavior] is null then it will fallback to
382+
/// [scrollBehavior]. If that is also null, the inherited
383+
/// [ScrollBehavior.getKeyboardDismissBehavior] will be used.
384+
final ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
381385

382386
/// {@macro flutter.widgets.scrollable.restorationId}
383387
final String? restorationId;
@@ -505,7 +509,12 @@ abstract class ScrollView extends StatelessWidget {
505509
? PrimaryScrollController.none(child: scrollable)
506510
: scrollable;
507511

508-
if (keyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) {
512+
final ScrollViewKeyboardDismissBehavior effectiveKeyboardDismissBehavior =
513+
keyboardDismissBehavior ??
514+
scrollBehavior?.getKeyboardDismissBehavior(context) ??
515+
ScrollConfiguration.of(context).getKeyboardDismissBehavior(context);
516+
517+
if (effectiveKeyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) {
509518
return NotificationListener<ScrollUpdateNotification>(
510519
child: scrollableResult,
511520
onNotification: (ScrollUpdateNotification notification) {

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import 'focus_scope.dart';
1818
import 'framework.dart';
1919
import 'notification_listener.dart';
2020
import 'primary_scroll_controller.dart';
21+
import 'scroll_configuration.dart';
2122
import 'scroll_controller.dart';
2223
import 'scroll_notification.dart';
2324
import 'scroll_physics.dart';
@@ -158,7 +159,7 @@ class SingleChildScrollView extends StatelessWidget {
158159
this.clipBehavior = Clip.hardEdge,
159160
this.hitTestBehavior = HitTestBehavior.opaque,
160161
this.restorationId,
161-
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
162+
this.keyboardDismissBehavior,
162163
}) : assert(
163164
!(controller != null && (primary ?? false)),
164165
'Primary ScrollViews obtain their ScrollController via inheritance '
@@ -233,7 +234,10 @@ class SingleChildScrollView extends StatelessWidget {
233234
final String? restorationId;
234235

235236
/// {@macro flutter.widgets.scroll_view.keyboardDismissBehavior}
236-
final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
237+
///
238+
/// If [keyboardDismissBehavior] is null then it will fallback to the inherited
239+
/// [ScrollBehavior.getKeyboardDismissBehavior].
240+
final ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
237241

238242
AxisDirection _getDirection(BuildContext context) {
239243
return getAxisDirectionFromAxisReverseAndDirectionality(context, scrollDirection, reverse);
@@ -271,7 +275,11 @@ class SingleChildScrollView extends StatelessWidget {
271275
},
272276
);
273277

274-
if (keyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) {
278+
final ScrollViewKeyboardDismissBehavior effectiveKeyboardDismissBehavior =
279+
keyboardDismissBehavior ??
280+
ScrollConfiguration.of(context).getKeyboardDismissBehavior(context);
281+
282+
if (effectiveKeyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) {
275283
scrollable = NotificationListener<ScrollUpdateNotification>(
276284
child: scrollable,
277285
onNotification: (ScrollUpdateNotification notification) {

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'focus_scope.dart';
1313
import 'framework.dart';
1414
import 'notification_listener.dart';
1515
import 'primary_scroll_controller.dart';
16+
import 'scroll_configuration.dart';
1617
import 'scroll_controller.dart';
1718
import 'scroll_delegate.dart';
1819
import 'scroll_notification.dart';
@@ -63,7 +64,7 @@ abstract class TwoDimensionalScrollView extends StatelessWidget {
6364
this.cacheExtent,
6465
this.diagonalDragBehavior = DiagonalDragBehavior.none,
6566
this.dragStartBehavior = DragStartBehavior.start,
66-
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
67+
this.keyboardDismissBehavior,
6768
this.clipBehavior = Clip.hardEdge,
6869
this.hitTestBehavior = HitTestBehavior.opaque,
6970
});
@@ -109,7 +110,10 @@ abstract class TwoDimensionalScrollView extends StatelessWidget {
109110
final DragStartBehavior dragStartBehavior;
110111

111112
/// {@macro flutter.widgets.scroll_view.keyboardDismissBehavior}
112-
final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
113+
///
114+
/// If [keyboardDismissBehavior] is null then it will fallback to the inherited
115+
/// [ScrollBehavior.getKeyboardDismissBehavior].
116+
final ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
113117

114118
/// {@macro flutter.widgets.scrollable.hitTestBehavior}
115119
///
@@ -187,7 +191,11 @@ abstract class TwoDimensionalScrollView extends StatelessWidget {
187191
? PrimaryScrollController.none(child: scrollable)
188192
: scrollable;
189193

190-
if (keyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) {
194+
final ScrollViewKeyboardDismissBehavior effectiveKeyboardDismissBehavior =
195+
keyboardDismissBehavior ??
196+
ScrollConfiguration.of(context).getKeyboardDismissBehavior(context);
197+
198+
if (effectiveKeyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) {
191199
return NotificationListener<ScrollUpdateNotification>(
192200
child: scrollableResult,
193201
onNotification: (ScrollUpdateNotification notification) {

packages/flutter/test/cupertino/app_test.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,51 @@ void main() {
392392
);
393393
});
394394

395+
testWidgets('CupertinoApp has correct default KeyboardDismissBehavior', (
396+
WidgetTester tester,
397+
) async {
398+
late BuildContext capturedContext;
399+
await tester.pumpWidget(
400+
CupertinoApp(
401+
home: Builder(
402+
builder: (BuildContext context) {
403+
capturedContext = context;
404+
return const Placeholder();
405+
},
406+
),
407+
),
408+
);
409+
410+
expect(
411+
ScrollConfiguration.of(capturedContext).getKeyboardDismissBehavior(capturedContext),
412+
ScrollViewKeyboardDismissBehavior.manual,
413+
);
414+
});
415+
416+
testWidgets('CupertinoApp can override default KeyboardDismissBehavior', (
417+
WidgetTester tester,
418+
) async {
419+
late BuildContext capturedContext;
420+
await tester.pumpWidget(
421+
CupertinoApp(
422+
scrollBehavior: const CupertinoScrollBehavior().copyWith(
423+
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
424+
),
425+
home: Builder(
426+
builder: (BuildContext context) {
427+
capturedContext = context;
428+
return const Placeholder();
429+
},
430+
),
431+
),
432+
);
433+
434+
expect(
435+
ScrollConfiguration.of(capturedContext).getKeyboardDismissBehavior(capturedContext),
436+
ScrollViewKeyboardDismissBehavior.onDrag,
437+
);
438+
});
439+
395440
testWidgets('A ScrollBehavior can be set for CupertinoApp', (WidgetTester tester) async {
396441
late BuildContext capturedContext;
397442
await tester.pumpWidget(

packages/flutter/test/material/app_test.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,51 @@ void main() {
12481248
expect(ScrollConfiguration.of(capturedContext).runtimeType, MaterialScrollBehavior);
12491249
});
12501250

1251+
testWidgets('MaterialApp has correct default KeyboardDismissBehavior', (
1252+
WidgetTester tester,
1253+
) async {
1254+
late BuildContext capturedContext;
1255+
await tester.pumpWidget(
1256+
MaterialApp(
1257+
home: Builder(
1258+
builder: (BuildContext context) {
1259+
capturedContext = context;
1260+
return const Placeholder();
1261+
},
1262+
),
1263+
),
1264+
);
1265+
1266+
expect(
1267+
ScrollConfiguration.of(capturedContext).getKeyboardDismissBehavior(capturedContext),
1268+
ScrollViewKeyboardDismissBehavior.manual,
1269+
);
1270+
});
1271+
1272+
testWidgets('MaterialApp can override default KeyboardDismissBehavior', (
1273+
WidgetTester tester,
1274+
) async {
1275+
late BuildContext capturedContext;
1276+
await tester.pumpWidget(
1277+
MaterialApp(
1278+
scrollBehavior: const MaterialScrollBehavior().copyWith(
1279+
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
1280+
),
1281+
home: Builder(
1282+
builder: (BuildContext context) {
1283+
capturedContext = context;
1284+
return const Placeholder();
1285+
},
1286+
),
1287+
),
1288+
);
1289+
1290+
expect(
1291+
ScrollConfiguration.of(capturedContext).getKeyboardDismissBehavior(capturedContext),
1292+
ScrollViewKeyboardDismissBehavior.onDrag,
1293+
);
1294+
});
1295+
12511296
testWidgets('A ScrollBehavior can be set for MaterialApp', (WidgetTester tester) async {
12521297
late BuildContext capturedContext;
12531298
await tester.pumpWidget(

0 commit comments

Comments
 (0)