blob: a1f6afa0a271a3d3f19f1846ef248161fdf9d4d5 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
late GestureVelocityTrackerBuilder lastCreatedBuilder;
class TestScrollBehavior extends ScrollBehavior {
const TestScrollBehavior(this.flag);
final bool flag;
@override
ScrollPhysics getScrollPhysics(BuildContext context) {
return flag
? const ClampingScrollPhysics()
: const BouncingScrollPhysics();
}
@override
bool shouldNotify(TestScrollBehavior old) => flag != old.flag;
@override
GestureVelocityTrackerBuilder velocityTrackerBuilder(BuildContext context) {
lastCreatedBuilder = flag
? (PointerEvent ev) => VelocityTracker.withKind(ev.kind)
: (PointerEvent ev) => IOSScrollViewFlingVelocityTracker(ev.kind);
return lastCreatedBuilder;
}
}
void main() {
testWidgets('Inherited ScrollConfiguration changed', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(debugLabel: 'scrollable');
TestScrollBehavior? behavior;
late ScrollPositionWithSingleContext position;
final Widget scrollView = SingleChildScrollView(
key: key,
child: Builder(
builder: (BuildContext context) {
behavior = ScrollConfiguration.of(context) as TestScrollBehavior;
position = Scrollable.of(context)!.position as ScrollPositionWithSingleContext;
return Container(height: 1000.0);
},
),
);
await tester.pumpWidget(
ScrollConfiguration(
behavior: const TestScrollBehavior(true),
child: scrollView,
),
);
expect(behavior, isNotNull);
expect(behavior!.flag, isTrue);
expect(position.physics, isA<ClampingScrollPhysics>());
expect(lastCreatedBuilder(const PointerDownEvent()), isA<VelocityTracker>());
ScrollMetrics metrics = position.copyWith();
expect(metrics.extentAfter, equals(400.0));
expect(metrics.viewportDimension, equals(600.0));
// Same Scrollable, different ScrollConfiguration
await tester.pumpWidget(
ScrollConfiguration(
behavior: const TestScrollBehavior(false),
child: scrollView,
),
);
expect(behavior, isNotNull);
expect(behavior!.flag, isFalse);
expect(position.physics, isA<BouncingScrollPhysics>());
expect(lastCreatedBuilder(const PointerDownEvent()), isA<IOSScrollViewFlingVelocityTracker>());
// Regression test for https://github.com/flutter/flutter/issues/5856
metrics = position.copyWith();
expect(metrics.extentAfter, equals(400.0));
expect(metrics.viewportDimension, equals(600.0));
});
testWidgets('ScrollBehavior default android overscroll indicator', (WidgetTester tester) async {
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: ScrollConfiguration(
behavior: const ScrollBehavior(),
child: ListView(
children: const <Widget>[
SizedBox(
height: 1000.0,
width: 1000.0,
child: Text('Test'),
)
]
)
),
));
expect(find.byType(StretchingOverscrollIndicator), findsNothing);
expect(find.byType(GlowingOverscrollIndicator), findsOneWidget);
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
testWidgets('ScrollBehavior stretch android overscroll indicator', (WidgetTester tester) async {
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(size: Size(800, 600)),
child: ScrollConfiguration(
behavior: const ScrollBehavior(androidOverscrollIndicator: AndroidOverscrollIndicator.stretch),
child: ListView(
children: const <Widget>[
SizedBox(
height: 1000.0,
width: 1000.0,
child: Text('Test'),
)
]
)
),
),
));
expect(find.byType(StretchingOverscrollIndicator), findsOneWidget);
expect(find.byType(GlowingOverscrollIndicator), findsNothing);
}, 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));
});
}