feat: Change default value of keyboardDismissBehavior (#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 : https://github.com/flutter/flutter/issues/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>
This commit is contained in:
committed by
GitHub
parent
36eb78b1b4
commit
5517cc9b3b
@@ -98,7 +98,7 @@ class ReorderableListView extends StatefulWidget {
|
||||
this.anchor = 0.0,
|
||||
this.cacheExtent,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
||||
this.keyboardDismissBehavior,
|
||||
this.restorationId,
|
||||
this.clipBehavior = Clip.hardEdge,
|
||||
this.autoScrollerVelocityScalar,
|
||||
@@ -169,7 +169,7 @@ class ReorderableListView extends StatefulWidget {
|
||||
this.anchor = 0.0,
|
||||
this.cacheExtent,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
||||
this.keyboardDismissBehavior,
|
||||
this.restorationId,
|
||||
this.clipBehavior = Clip.hardEdge,
|
||||
this.autoScrollerVelocityScalar,
|
||||
@@ -271,8 +271,9 @@ class ReorderableListView extends StatefulWidget {
|
||||
|
||||
/// {@macro flutter.widgets.scroll_view.keyboardDismissBehavior}
|
||||
///
|
||||
/// The default is [ScrollViewKeyboardDismissBehavior.manual]
|
||||
final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
|
||||
/// If [keyboardDismissBehavior] is null then it will fallback to the inherited
|
||||
/// [ScrollBehavior.getKeyboardDismissBehavior].
|
||||
final ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
|
||||
|
||||
/// {@macro flutter.widgets.scrollable.restorationId}
|
||||
final String? restorationId;
|
||||
|
||||
@@ -168,7 +168,7 @@ class ReorderableList extends StatefulWidget {
|
||||
this.anchor = 0.0,
|
||||
this.cacheExtent,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
||||
this.keyboardDismissBehavior,
|
||||
this.restorationId,
|
||||
this.clipBehavior = Clip.hardEdge,
|
||||
this.autoScrollerVelocityScalar,
|
||||
@@ -280,8 +280,9 @@ class ReorderableList extends StatefulWidget {
|
||||
|
||||
/// {@macro flutter.widgets.scroll_view.keyboardDismissBehavior}
|
||||
///
|
||||
/// The default is [ScrollViewKeyboardDismissBehavior.manual]
|
||||
final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
|
||||
/// If [keyboardDismissBehavior] is null then it will fallback to the inherited
|
||||
/// [ScrollBehavior.getKeyboardDismissBehavior].
|
||||
final ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
|
||||
|
||||
/// {@macro flutter.widgets.scrollable.restorationId}
|
||||
final String? restorationId;
|
||||
|
||||
@@ -18,6 +18,7 @@ import 'package:flutter/services.dart' show LogicalKeyboardKey;
|
||||
import 'framework.dart';
|
||||
import 'overscroll_indicator.dart';
|
||||
import 'scroll_physics.dart';
|
||||
import 'scroll_view.dart';
|
||||
import 'scrollable.dart';
|
||||
import 'scrollable_helpers.dart';
|
||||
import 'scrollbar.dart';
|
||||
@@ -89,6 +90,7 @@ class ScrollBehavior {
|
||||
Set<LogicalKeyboardKey>? pointerAxisModifiers,
|
||||
ScrollPhysics? physics,
|
||||
TargetPlatform? platform,
|
||||
ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior,
|
||||
}) {
|
||||
return _WrappedScrollBehavior(
|
||||
delegate: this,
|
||||
@@ -99,6 +101,7 @@ class ScrollBehavior {
|
||||
pointerAxisModifiers: pointerAxisModifiers,
|
||||
physics: physics,
|
||||
platform: platform,
|
||||
keyboardDismissBehavior: keyboardDismissBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -264,6 +267,12 @@ class ScrollBehavior {
|
||||
/// method returns false, the rebuilds might be optimized away.
|
||||
bool shouldNotify(covariant ScrollBehavior oldDelegate) => false;
|
||||
|
||||
/// The default keyboard dismissal behavior for [ScrollView] widgets.
|
||||
///
|
||||
/// Defaults to [ScrollViewKeyboardDismissBehavior.manual].
|
||||
ScrollViewKeyboardDismissBehavior getKeyboardDismissBehavior(BuildContext context) =>
|
||||
ScrollViewKeyboardDismissBehavior.manual;
|
||||
|
||||
@override
|
||||
String toString() => objectRuntimeType(this, 'ScrollBehavior');
|
||||
}
|
||||
@@ -278,6 +287,7 @@ class _WrappedScrollBehavior implements ScrollBehavior {
|
||||
Set<LogicalKeyboardKey>? pointerAxisModifiers,
|
||||
this.physics,
|
||||
this.platform,
|
||||
this.keyboardDismissBehavior,
|
||||
}) : _dragDevices = dragDevices,
|
||||
_pointerAxisModifiers = pointerAxisModifiers;
|
||||
|
||||
@@ -286,6 +296,7 @@ class _WrappedScrollBehavior implements ScrollBehavior {
|
||||
final bool overscroll;
|
||||
final ScrollPhysics? physics;
|
||||
final TargetPlatform? platform;
|
||||
final ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
|
||||
final Set<PointerDeviceKind>? _dragDevices;
|
||||
final MultitouchDragStrategy? multitouchDragStrategy;
|
||||
final Set<LogicalKeyboardKey>? _pointerAxisModifiers;
|
||||
@@ -327,6 +338,7 @@ class _WrappedScrollBehavior implements ScrollBehavior {
|
||||
Set<LogicalKeyboardKey>? pointerAxisModifiers,
|
||||
ScrollPhysics? physics,
|
||||
TargetPlatform? platform,
|
||||
ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior,
|
||||
}) {
|
||||
return delegate.copyWith(
|
||||
scrollbars: scrollbars ?? this.scrollbars,
|
||||
@@ -336,6 +348,7 @@ class _WrappedScrollBehavior implements ScrollBehavior {
|
||||
pointerAxisModifiers: pointerAxisModifiers ?? this.pointerAxisModifiers,
|
||||
physics: physics ?? this.physics,
|
||||
platform: platform ?? this.platform,
|
||||
keyboardDismissBehavior: keyboardDismissBehavior ?? this.keyboardDismissBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -349,6 +362,11 @@ class _WrappedScrollBehavior implements ScrollBehavior {
|
||||
return physics ?? delegate.getScrollPhysics(context);
|
||||
}
|
||||
|
||||
@override
|
||||
ScrollViewKeyboardDismissBehavior getKeyboardDismissBehavior(BuildContext context) {
|
||||
return keyboardDismissBehavior ?? delegate.getKeyboardDismissBehavior(context);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldNotify(_WrappedScrollBehavior oldDelegate) {
|
||||
return oldDelegate.delegate.runtimeType != delegate.runtimeType ||
|
||||
|
||||
@@ -118,7 +118,7 @@ abstract class ScrollView extends StatelessWidget {
|
||||
this.cacheExtent,
|
||||
this.semanticChildCount,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
||||
this.keyboardDismissBehavior,
|
||||
this.restorationId,
|
||||
this.clipBehavior = Clip.hardEdge,
|
||||
this.hitTestBehavior = HitTestBehavior.opaque,
|
||||
@@ -374,10 +374,14 @@ abstract class ScrollView extends StatelessWidget {
|
||||
final DragStartBehavior dragStartBehavior;
|
||||
|
||||
/// {@template flutter.widgets.scroll_view.keyboardDismissBehavior}
|
||||
/// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will
|
||||
/// The [ScrollViewKeyboardDismissBehavior] defines how this [ScrollView] will
|
||||
/// dismiss the keyboard automatically.
|
||||
/// {@endtemplate}
|
||||
final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
|
||||
///
|
||||
/// If [keyboardDismissBehavior] is null then it will fallback to
|
||||
/// [scrollBehavior]. If that is also null, the inherited
|
||||
/// [ScrollBehavior.getKeyboardDismissBehavior] will be used.
|
||||
final ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
|
||||
|
||||
/// {@macro flutter.widgets.scrollable.restorationId}
|
||||
final String? restorationId;
|
||||
@@ -505,7 +509,12 @@ abstract class ScrollView extends StatelessWidget {
|
||||
? PrimaryScrollController.none(child: scrollable)
|
||||
: scrollable;
|
||||
|
||||
if (keyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) {
|
||||
final ScrollViewKeyboardDismissBehavior effectiveKeyboardDismissBehavior =
|
||||
keyboardDismissBehavior ??
|
||||
scrollBehavior?.getKeyboardDismissBehavior(context) ??
|
||||
ScrollConfiguration.of(context).getKeyboardDismissBehavior(context);
|
||||
|
||||
if (effectiveKeyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) {
|
||||
return NotificationListener<ScrollUpdateNotification>(
|
||||
child: scrollableResult,
|
||||
onNotification: (ScrollUpdateNotification notification) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import 'focus_scope.dart';
|
||||
import 'framework.dart';
|
||||
import 'notification_listener.dart';
|
||||
import 'primary_scroll_controller.dart';
|
||||
import 'scroll_configuration.dart';
|
||||
import 'scroll_controller.dart';
|
||||
import 'scroll_notification.dart';
|
||||
import 'scroll_physics.dart';
|
||||
@@ -158,7 +159,7 @@ class SingleChildScrollView extends StatelessWidget {
|
||||
this.clipBehavior = Clip.hardEdge,
|
||||
this.hitTestBehavior = HitTestBehavior.opaque,
|
||||
this.restorationId,
|
||||
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
||||
this.keyboardDismissBehavior,
|
||||
}) : assert(
|
||||
!(controller != null && (primary ?? false)),
|
||||
'Primary ScrollViews obtain their ScrollController via inheritance '
|
||||
@@ -233,7 +234,10 @@ class SingleChildScrollView extends StatelessWidget {
|
||||
final String? restorationId;
|
||||
|
||||
/// {@macro flutter.widgets.scroll_view.keyboardDismissBehavior}
|
||||
final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
|
||||
///
|
||||
/// If [keyboardDismissBehavior] is null then it will fallback to the inherited
|
||||
/// [ScrollBehavior.getKeyboardDismissBehavior].
|
||||
final ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
|
||||
|
||||
AxisDirection _getDirection(BuildContext context) {
|
||||
return getAxisDirectionFromAxisReverseAndDirectionality(context, scrollDirection, reverse);
|
||||
@@ -271,7 +275,11 @@ class SingleChildScrollView extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
|
||||
if (keyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) {
|
||||
final ScrollViewKeyboardDismissBehavior effectiveKeyboardDismissBehavior =
|
||||
keyboardDismissBehavior ??
|
||||
ScrollConfiguration.of(context).getKeyboardDismissBehavior(context);
|
||||
|
||||
if (effectiveKeyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) {
|
||||
scrollable = NotificationListener<ScrollUpdateNotification>(
|
||||
child: scrollable,
|
||||
onNotification: (ScrollUpdateNotification notification) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'focus_scope.dart';
|
||||
import 'framework.dart';
|
||||
import 'notification_listener.dart';
|
||||
import 'primary_scroll_controller.dart';
|
||||
import 'scroll_configuration.dart';
|
||||
import 'scroll_controller.dart';
|
||||
import 'scroll_delegate.dart';
|
||||
import 'scroll_notification.dart';
|
||||
@@ -63,7 +64,7 @@ abstract class TwoDimensionalScrollView extends StatelessWidget {
|
||||
this.cacheExtent,
|
||||
this.diagonalDragBehavior = DiagonalDragBehavior.none,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
||||
this.keyboardDismissBehavior,
|
||||
this.clipBehavior = Clip.hardEdge,
|
||||
this.hitTestBehavior = HitTestBehavior.opaque,
|
||||
});
|
||||
@@ -109,7 +110,10 @@ abstract class TwoDimensionalScrollView extends StatelessWidget {
|
||||
final DragStartBehavior dragStartBehavior;
|
||||
|
||||
/// {@macro flutter.widgets.scroll_view.keyboardDismissBehavior}
|
||||
final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
|
||||
///
|
||||
/// If [keyboardDismissBehavior] is null then it will fallback to the inherited
|
||||
/// [ScrollBehavior.getKeyboardDismissBehavior].
|
||||
final ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
|
||||
|
||||
/// {@macro flutter.widgets.scrollable.hitTestBehavior}
|
||||
///
|
||||
@@ -187,7 +191,11 @@ abstract class TwoDimensionalScrollView extends StatelessWidget {
|
||||
? PrimaryScrollController.none(child: scrollable)
|
||||
: scrollable;
|
||||
|
||||
if (keyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) {
|
||||
final ScrollViewKeyboardDismissBehavior effectiveKeyboardDismissBehavior =
|
||||
keyboardDismissBehavior ??
|
||||
ScrollConfiguration.of(context).getKeyboardDismissBehavior(context);
|
||||
|
||||
if (effectiveKeyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) {
|
||||
return NotificationListener<ScrollUpdateNotification>(
|
||||
child: scrollableResult,
|
||||
onNotification: (ScrollUpdateNotification notification) {
|
||||
|
||||
@@ -392,6 +392,51 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('CupertinoApp has correct default KeyboardDismissBehavior', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
late BuildContext capturedContext;
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: Builder(
|
||||
builder: (BuildContext context) {
|
||||
capturedContext = context;
|
||||
return const Placeholder();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
ScrollConfiguration.of(capturedContext).getKeyboardDismissBehavior(capturedContext),
|
||||
ScrollViewKeyboardDismissBehavior.manual,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('CupertinoApp can override default KeyboardDismissBehavior', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
late BuildContext capturedContext;
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
scrollBehavior: const CupertinoScrollBehavior().copyWith(
|
||||
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
|
||||
),
|
||||
home: Builder(
|
||||
builder: (BuildContext context) {
|
||||
capturedContext = context;
|
||||
return const Placeholder();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
ScrollConfiguration.of(capturedContext).getKeyboardDismissBehavior(capturedContext),
|
||||
ScrollViewKeyboardDismissBehavior.onDrag,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('A ScrollBehavior can be set for CupertinoApp', (WidgetTester tester) async {
|
||||
late BuildContext capturedContext;
|
||||
await tester.pumpWidget(
|
||||
|
||||
@@ -1248,6 +1248,51 @@ void main() {
|
||||
expect(ScrollConfiguration.of(capturedContext).runtimeType, MaterialScrollBehavior);
|
||||
});
|
||||
|
||||
testWidgets('MaterialApp has correct default KeyboardDismissBehavior', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
late BuildContext capturedContext;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Builder(
|
||||
builder: (BuildContext context) {
|
||||
capturedContext = context;
|
||||
return const Placeholder();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
ScrollConfiguration.of(capturedContext).getKeyboardDismissBehavior(capturedContext),
|
||||
ScrollViewKeyboardDismissBehavior.manual,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('MaterialApp can override default KeyboardDismissBehavior', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
late BuildContext capturedContext;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
scrollBehavior: const MaterialScrollBehavior().copyWith(
|
||||
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
|
||||
),
|
||||
home: Builder(
|
||||
builder: (BuildContext context) {
|
||||
capturedContext = context;
|
||||
return const Placeholder();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
ScrollConfiguration.of(capturedContext).getKeyboardDismissBehavior(capturedContext),
|
||||
ScrollViewKeyboardDismissBehavior.onDrag,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('A ScrollBehavior can be set for MaterialApp', (WidgetTester tester) async {
|
||||
late BuildContext capturedContext;
|
||||
await tester.pumpWidget(
|
||||
|
||||
Reference in New Issue
Block a user