Fix ScrollBehavior copyWith (#91834)
This commit is contained in:
parent
b11e962062
commit
48b51c3f72
@ -85,19 +85,21 @@ class ScrollBehavior {
|
|||||||
/// the widget level, like [PageView.scrollBehavior], in order to change the
|
/// the widget level, like [PageView.scrollBehavior], in order to change the
|
||||||
/// default.
|
/// default.
|
||||||
ScrollBehavior copyWith({
|
ScrollBehavior copyWith({
|
||||||
bool scrollbars = true,
|
bool? scrollbars,
|
||||||
bool overscroll = true,
|
bool? overscroll,
|
||||||
Set<PointerDeviceKind>? dragDevices,
|
Set<PointerDeviceKind>? dragDevices,
|
||||||
ScrollPhysics? physics,
|
ScrollPhysics? physics,
|
||||||
TargetPlatform? platform,
|
TargetPlatform? platform,
|
||||||
|
AndroidOverscrollIndicator? androidOverscrollIndicator,
|
||||||
}) {
|
}) {
|
||||||
return _WrappedScrollBehavior(
|
return _WrappedScrollBehavior(
|
||||||
delegate: this,
|
delegate: this,
|
||||||
scrollbar: scrollbars,
|
scrollbars: scrollbars ?? true,
|
||||||
overscrollIndicator: overscroll,
|
overscroll: overscroll ?? true,
|
||||||
physics: physics,
|
physics: physics,
|
||||||
platform: platform,
|
platform: platform,
|
||||||
dragDevices: dragDevices,
|
dragDevices: dragDevices,
|
||||||
|
androidOverscrollIndicator: androidOverscrollIndicator
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,38 +253,40 @@ class ScrollBehavior {
|
|||||||
class _WrappedScrollBehavior implements ScrollBehavior {
|
class _WrappedScrollBehavior implements ScrollBehavior {
|
||||||
const _WrappedScrollBehavior({
|
const _WrappedScrollBehavior({
|
||||||
required this.delegate,
|
required this.delegate,
|
||||||
this.scrollbar = true,
|
this.scrollbars = true,
|
||||||
this.overscrollIndicator = true,
|
this.overscroll = true,
|
||||||
this.physics,
|
this.physics,
|
||||||
this.platform,
|
this.platform,
|
||||||
Set<PointerDeviceKind>? dragDevices,
|
Set<PointerDeviceKind>? dragDevices,
|
||||||
}) : _dragDevices = dragDevices;
|
AndroidOverscrollIndicator? androidOverscrollIndicator,
|
||||||
|
}) : _androidOverscrollIndicator = androidOverscrollIndicator,
|
||||||
|
_dragDevices = dragDevices;
|
||||||
|
|
||||||
final ScrollBehavior delegate;
|
final ScrollBehavior delegate;
|
||||||
final bool scrollbar;
|
final bool scrollbars;
|
||||||
final bool overscrollIndicator;
|
final bool overscroll;
|
||||||
final ScrollPhysics? physics;
|
final ScrollPhysics? physics;
|
||||||
final TargetPlatform? platform;
|
final TargetPlatform? platform;
|
||||||
final Set<PointerDeviceKind>? _dragDevices;
|
final Set<PointerDeviceKind>? _dragDevices;
|
||||||
|
@override
|
||||||
|
final AndroidOverscrollIndicator? _androidOverscrollIndicator;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Set<PointerDeviceKind> get dragDevices => _dragDevices ?? delegate.dragDevices;
|
Set<PointerDeviceKind> get dragDevices => _dragDevices ?? delegate.dragDevices;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
AndroidOverscrollIndicator get androidOverscrollIndicator => delegate.androidOverscrollIndicator;
|
AndroidOverscrollIndicator get androidOverscrollIndicator => _androidOverscrollIndicator ?? delegate.androidOverscrollIndicator;
|
||||||
@override
|
|
||||||
AndroidOverscrollIndicator? get _androidOverscrollIndicator => throw UnimplementedError();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildOverscrollIndicator(BuildContext context, Widget child, ScrollableDetails details) {
|
Widget buildOverscrollIndicator(BuildContext context, Widget child, ScrollableDetails details) {
|
||||||
if (overscrollIndicator)
|
if (overscroll)
|
||||||
return delegate.buildOverscrollIndicator(context, child, details);
|
return delegate.buildOverscrollIndicator(context, child, details);
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildScrollbar(BuildContext context, Widget child, ScrollableDetails details) {
|
Widget buildScrollbar(BuildContext context, Widget child, ScrollableDetails details) {
|
||||||
if (scrollbar)
|
if (scrollbars)
|
||||||
return delegate.buildScrollbar(context, child, details);
|
return delegate.buildScrollbar(context, child, details);
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
@ -294,19 +298,20 @@ class _WrappedScrollBehavior implements ScrollBehavior {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
ScrollBehavior copyWith({
|
ScrollBehavior copyWith({
|
||||||
bool scrollbars = true,
|
bool? scrollbars,
|
||||||
bool overscroll = true,
|
bool? overscroll,
|
||||||
ScrollPhysics? physics,
|
ScrollPhysics? physics,
|
||||||
TargetPlatform? platform,
|
TargetPlatform? platform,
|
||||||
Set<PointerDeviceKind>? dragDevices,
|
Set<PointerDeviceKind>? dragDevices,
|
||||||
AndroidOverscrollIndicator? androidOverscrollIndicator
|
AndroidOverscrollIndicator? androidOverscrollIndicator
|
||||||
}) {
|
}) {
|
||||||
return delegate.copyWith(
|
return delegate.copyWith(
|
||||||
scrollbars: scrollbars,
|
scrollbars: scrollbars ?? this.scrollbars,
|
||||||
overscroll: overscroll,
|
overscroll: overscroll ?? this.overscroll,
|
||||||
physics: physics,
|
physics: physics ?? this.physics,
|
||||||
platform: platform,
|
platform: platform ?? this.platform,
|
||||||
dragDevices: dragDevices,
|
dragDevices: dragDevices ?? this.dragDevices,
|
||||||
|
androidOverscrollIndicator: androidOverscrollIndicator ?? this.androidOverscrollIndicator,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,8 +328,8 @@ class _WrappedScrollBehavior implements ScrollBehavior {
|
|||||||
@override
|
@override
|
||||||
bool shouldNotify(_WrappedScrollBehavior oldDelegate) {
|
bool shouldNotify(_WrappedScrollBehavior oldDelegate) {
|
||||||
return oldDelegate.delegate.runtimeType != delegate.runtimeType
|
return oldDelegate.delegate.runtimeType != delegate.runtimeType
|
||||||
|| oldDelegate.scrollbar != scrollbar
|
|| oldDelegate.scrollbars != scrollbars
|
||||||
|| oldDelegate.overscrollIndicator != overscrollIndicator
|
|| oldDelegate.overscroll != overscroll
|
||||||
|| oldDelegate.physics != physics
|
|| oldDelegate.physics != physics
|
||||||
|| oldDelegate.platform != platform
|
|| oldDelegate.platform != platform
|
||||||
|| setEquals<PointerDeviceKind>(oldDelegate.dragDevices, dragDevices)
|
|| setEquals<PointerDeviceKind>(oldDelegate.dragDevices, dragDevices)
|
||||||
|
@ -125,4 +125,181 @@ void main() {
|
|||||||
expect(find.byType(StretchingOverscrollIndicator), findsOneWidget);
|
expect(find.byType(StretchingOverscrollIndicator), findsOneWidget);
|
||||||
expect(find.byType(GlowingOverscrollIndicator), findsNothing);
|
expect(find.byType(GlowingOverscrollIndicator), findsNothing);
|
||||||
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
||||||
|
|
||||||
|
group('ScrollBehavior configuration is maintained over multiple copies', () {
|
||||||
|
testWidgets('dragDevices', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/91673
|
||||||
|
const ScrollBehavior defaultBehavior = ScrollBehavior();
|
||||||
|
expect(defaultBehavior.dragDevices, <PointerDeviceKind>{
|
||||||
|
PointerDeviceKind.touch,
|
||||||
|
PointerDeviceKind.stylus,
|
||||||
|
PointerDeviceKind.invertedStylus,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use copyWith to modify drag devices
|
||||||
|
final ScrollBehavior onceCopiedBehavior = defaultBehavior.copyWith(
|
||||||
|
dragDevices: PointerDeviceKind.values.toSet(),
|
||||||
|
);
|
||||||
|
expect(onceCopiedBehavior.dragDevices, PointerDeviceKind.values.toSet());
|
||||||
|
|
||||||
|
// Copy again. The previously modified drag devices should carry over.
|
||||||
|
final ScrollBehavior twiceCopiedBehavior = onceCopiedBehavior.copyWith();
|
||||||
|
expect(twiceCopiedBehavior.dragDevices, PointerDeviceKind.values.toSet());
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('androidOverscrollIndicator', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/91673
|
||||||
|
const ScrollBehavior defaultBehavior = ScrollBehavior();
|
||||||
|
expect(defaultBehavior.androidOverscrollIndicator, AndroidOverscrollIndicator.glow);
|
||||||
|
|
||||||
|
// Use copyWith to modify androidOverscrollIndicator
|
||||||
|
final ScrollBehavior onceCopiedBehavior = defaultBehavior.copyWith(
|
||||||
|
androidOverscrollIndicator: AndroidOverscrollIndicator.stretch,
|
||||||
|
);
|
||||||
|
expect(onceCopiedBehavior.androidOverscrollIndicator, AndroidOverscrollIndicator.stretch);
|
||||||
|
|
||||||
|
// Copy again. The previously modified value should carry over.
|
||||||
|
final ScrollBehavior twiceCopiedBehavior = onceCopiedBehavior.copyWith();
|
||||||
|
expect(twiceCopiedBehavior.androidOverscrollIndicator, AndroidOverscrollIndicator.stretch);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('physics', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/91673
|
||||||
|
late ScrollPhysics defaultPhysics;
|
||||||
|
late ScrollPhysics onceCopiedPhysics;
|
||||||
|
late ScrollPhysics twiceCopiedPhysics;
|
||||||
|
|
||||||
|
await tester.pumpWidget(ScrollConfiguration(
|
||||||
|
// Default ScrollBehavior
|
||||||
|
behavior: const ScrollBehavior(),
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
final ScrollBehavior defaultBehavior = ScrollConfiguration.of(context);
|
||||||
|
// Copy once to change physics
|
||||||
|
defaultPhysics = defaultBehavior.getScrollPhysics(context);
|
||||||
|
return ScrollConfiguration(
|
||||||
|
behavior: defaultBehavior.copyWith(physics: const BouncingScrollPhysics()),
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
final ScrollBehavior onceCopiedBehavior = ScrollConfiguration.of(context);
|
||||||
|
onceCopiedPhysics = onceCopiedBehavior.getScrollPhysics(context);
|
||||||
|
return ScrollConfiguration(
|
||||||
|
// Copy again, physics should follow
|
||||||
|
behavior: onceCopiedBehavior.copyWith(),
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
twiceCopiedPhysics = ScrollConfiguration.of(context).getScrollPhysics(context);
|
||||||
|
return SingleChildScrollView(child: Container(height: 1000));
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(defaultPhysics, const ClampingScrollPhysics(parent: RangeMaintainingScrollPhysics()));
|
||||||
|
expect(onceCopiedPhysics, const BouncingScrollPhysics());
|
||||||
|
expect(twiceCopiedPhysics, const BouncingScrollPhysics());
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('platform', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/91673
|
||||||
|
late TargetPlatform defaultPlatform;
|
||||||
|
late TargetPlatform onceCopiedPlatform;
|
||||||
|
late TargetPlatform twiceCopiedPlatform;
|
||||||
|
|
||||||
|
await tester.pumpWidget(ScrollConfiguration(
|
||||||
|
// Default ScrollBehavior
|
||||||
|
behavior: const ScrollBehavior(),
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
final ScrollBehavior defaultBehavior = ScrollConfiguration.of(context);
|
||||||
|
// Copy once to change physics
|
||||||
|
defaultPlatform = defaultBehavior.getPlatform(context);
|
||||||
|
return ScrollConfiguration(
|
||||||
|
behavior: defaultBehavior.copyWith(platform: TargetPlatform.fuchsia),
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
final ScrollBehavior onceCopiedBehavior = ScrollConfiguration.of(context);
|
||||||
|
onceCopiedPlatform = onceCopiedBehavior.getPlatform(context);
|
||||||
|
return ScrollConfiguration(
|
||||||
|
// Copy again, physics should follow
|
||||||
|
behavior: onceCopiedBehavior.copyWith(),
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
twiceCopiedPlatform = ScrollConfiguration.of(context).getPlatform(context);
|
||||||
|
return SingleChildScrollView(child: Container(height: 1000));
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(defaultPlatform, TargetPlatform.android);
|
||||||
|
expect(onceCopiedPlatform, TargetPlatform.fuchsia);
|
||||||
|
expect(twiceCopiedPlatform, TargetPlatform.fuchsia);
|
||||||
|
});
|
||||||
|
|
||||||
|
Widget wrap(ScrollBehavior behavior) {
|
||||||
|
return Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: MediaQuery(
|
||||||
|
data: const MediaQueryData(size: Size(500, 500)),
|
||||||
|
child: ScrollConfiguration(
|
||||||
|
behavior: behavior,
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) => SingleChildScrollView(child: Container(height: 1000))
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
testWidgets('scrollbar', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/91673
|
||||||
|
const ScrollBehavior defaultBehavior = ScrollBehavior();
|
||||||
|
await tester.pumpWidget(wrap(defaultBehavior));
|
||||||
|
// Default adds a scrollbar
|
||||||
|
expect(find.byType(RawScrollbar), findsOneWidget);
|
||||||
|
final ScrollBehavior onceCopiedBehavior = defaultBehavior.copyWith(scrollbars: false);
|
||||||
|
|
||||||
|
await tester.pumpWidget(wrap(onceCopiedBehavior));
|
||||||
|
// Copy does not add scrollbar
|
||||||
|
expect(find.byType(RawScrollbar), findsNothing);
|
||||||
|
final ScrollBehavior twiceCopiedBehavior = onceCopiedBehavior.copyWith();
|
||||||
|
|
||||||
|
await tester.pumpWidget(wrap(twiceCopiedBehavior));
|
||||||
|
// Second copy maintains scrollbar setting
|
||||||
|
expect(find.byType(RawScrollbar), findsNothing);
|
||||||
|
|
||||||
|
// For default scrollbars
|
||||||
|
}, variant: TargetPlatformVariant.desktop());
|
||||||
|
|
||||||
|
testWidgets('overscroll', (WidgetTester tester) async {
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/91673
|
||||||
|
const ScrollBehavior defaultBehavior = ScrollBehavior();
|
||||||
|
await tester.pumpWidget(wrap(defaultBehavior));
|
||||||
|
// Default adds a glowing overscroll indicator
|
||||||
|
expect(find.byType(GlowingOverscrollIndicator), findsOneWidget);
|
||||||
|
final ScrollBehavior onceCopiedBehavior = defaultBehavior.copyWith(overscroll: false);
|
||||||
|
|
||||||
|
await tester.pumpWidget(wrap(onceCopiedBehavior));
|
||||||
|
// Copy does not add indicator
|
||||||
|
expect(find.byType(GlowingOverscrollIndicator), findsNothing);
|
||||||
|
final ScrollBehavior twiceCopiedBehavior = onceCopiedBehavior.copyWith();
|
||||||
|
|
||||||
|
await tester.pumpWidget(wrap(twiceCopiedBehavior));
|
||||||
|
// Second copy maintains overscroll setting
|
||||||
|
expect(find.byType(GlowingOverscrollIndicator), findsNothing);
|
||||||
|
|
||||||
|
// For default glowing indicator
|
||||||
|
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user