362 lines
14 KiB
Dart
362 lines
14 KiB
Dart
// 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/foundation.dart';
|
|
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('Assert in buildScrollbar that controller != null when using it', (WidgetTester tester) async {
|
|
const ScrollBehavior defaultBehavior = ScrollBehavior();
|
|
late BuildContext capturedContext;
|
|
|
|
await tester.pumpWidget(ScrollConfiguration(
|
|
// Avoid the default ones here.
|
|
behavior: const ScrollBehavior().copyWith(scrollbars: false),
|
|
child: SingleChildScrollView(
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
capturedContext = context;
|
|
return Container(height: 1000.0);
|
|
},
|
|
),
|
|
),
|
|
));
|
|
|
|
const ScrollableDetails details = ScrollableDetails(direction: AxisDirection.down);
|
|
final Widget child = Container();
|
|
|
|
switch (defaultTargetPlatform) {
|
|
case TargetPlatform.android:
|
|
case TargetPlatform.fuchsia:
|
|
case TargetPlatform.iOS:
|
|
// Does not throw if we aren't using it.
|
|
defaultBehavior.buildScrollbar(capturedContext, child, details);
|
|
case TargetPlatform.linux:
|
|
case TargetPlatform.macOS:
|
|
case TargetPlatform.windows:
|
|
expect(
|
|
() {
|
|
defaultBehavior.buildScrollbar(capturedContext, child, details);
|
|
},
|
|
throwsA(
|
|
isA<AssertionError>().having((AssertionError error) => error.toString(),
|
|
'description', contains('details.controller != null')),
|
|
),
|
|
);
|
|
}
|
|
}, variant: TargetPlatformVariant.all());
|
|
|
|
// Regression test for https://github.com/flutter/flutter/issues/89681
|
|
testWidgets('_WrappedScrollBehavior shouldNotify test', (WidgetTester tester) async {
|
|
final ScrollBehavior behavior1 = const ScrollBehavior().copyWith();
|
|
final ScrollBehavior behavior2 = const ScrollBehavior().copyWith();
|
|
|
|
expect(behavior1.shouldNotify(behavior2), false);
|
|
});
|
|
|
|
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,
|
|
PointerDeviceKind.trackpad,
|
|
PointerDeviceKind.unknown,
|
|
});
|
|
|
|
// 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));
|
|
});
|
|
}
|