blob: 3443e0edc00403ce5ce6ab4439a4d2890d054cb1 [file] [log] [blame]
// Copyright 2016 The Chromium 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_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/physics.dart';
import 'package:flutter/material.dart';
class TestSliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
TestSliverPersistentHeaderDelegate(this._maxExtent);
final double _maxExtent;
@override
double get maxExtent => _maxExtent;
@override
double get minExtent => 16.0;
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return new Column(
children: <Widget>[
new Container(height: minExtent),
new Expanded(child: new Container()),
],
);
}
@override
bool shouldRebuild(TestSliverPersistentHeaderDelegate oldDelegate) => false;
}
class TestBehavior extends ScrollBehavior {
@override
Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) {
return new GlowingOverscrollIndicator(
child: child,
axisDirection: axisDirection,
color: const Color(0xFFFFFFFF),
);
}
}
class TestScrollPhysics extends ClampingScrollPhysics {
const TestScrollPhysics({ ScrollPhysics parent }) : super(parent: parent);
@override
TestScrollPhysics applyTo(ScrollPhysics ancestor) {
return new TestScrollPhysics(parent: parent?.applyTo(ancestor) ?? ancestor);
}
@override
Tolerance get tolerance => const Tolerance(velocity: 20.0, distance: 1.0);
}
class TestViewportScrollPosition extends ScrollPositionWithSingleContext {
TestViewportScrollPosition({
ScrollPhysics physics,
ScrollContext context,
ScrollPosition oldPosition,
}) : super(physics: physics, context: context, oldPosition: oldPosition);
@override
bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {
expect(minScrollExtent, moreOrLessEquals(-3895.0));
expect(maxScrollExtent, moreOrLessEquals(8575.0));
return super.applyContentDimensions(minScrollExtent, maxScrollExtent);
}
}
void main() {
testWidgets('Evil test of sliver features - 1', (WidgetTester tester) async {
final GlobalKey centerKey = new GlobalKey();
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new ScrollConfiguration(
behavior: new TestBehavior(),
child: new Scrollbar(
child: new Scrollable(
axisDirection: AxisDirection.down,
physics: const TestScrollPhysics(),
viewportBuilder: (BuildContext context, ViewportOffset offset) {
return new Viewport(
axisDirection: AxisDirection.down,
anchor: 0.25,
offset: offset,
center: centerKey,
slivers: <Widget>[
new SliverToBoxAdapter(child: new Container(height: 5.0)),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(150.0), pinned: true),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverPadding(
padding: const EdgeInsets.all(50.0),
sliver: new SliverToBoxAdapter(child: new Container(height: 520.0)),
),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(150.0), floating: true),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverToBoxAdapter(key: centerKey, child: new Container(height: 520.0)), // ------------------------ CENTER ------------------------
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(150.0), pinned: true),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverPadding(
padding: const EdgeInsets.all(50.0),
sliver: new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0), pinned: true),
),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0), pinned: true),
new SliverToBoxAdapter(child: new Container(height: 5.0)),
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0), pinned: true),
new SliverToBoxAdapter(child: new Container(height: 5.0)),
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0), pinned: true),
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0), pinned: true),
new SliverToBoxAdapter(child: new Container(height: 5.0)),
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0), pinned: true),
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0)),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(150.0), floating: true),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(150.0), floating: true),
new SliverToBoxAdapter(child: new Container(height: 5.0)),
new SliverList(
delegate: new SliverChildListDelegate(<Widget>[
new Container(height: 50.0),
new Container(height: 50.0),
new Container(height: 50.0),
new Container(height: 50.0),
new Container(height: 50.0),
new Container(height: 50.0),
new Container(height: 50.0),
new Container(height: 50.0),
new Container(height: 50.0),
new Container(height: 50.0),
new Container(height: 50.0),
new Container(height: 50.0),
new Container(height: 50.0),
new Container(height: 50.0),
new Container(height: 50.0),
]),
),
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0)),
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0)),
new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0)),
new SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 50.0),
sliver: new SliverToBoxAdapter(child: new Container(height: 520.0)),
),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverToBoxAdapter(child: new Container(height: 520.0)),
new SliverToBoxAdapter(child: new Container(height: 5.0)),
],
);
},
),
),
),
));
final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
position.animateTo(10000.0, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pump(const Duration(milliseconds: 10));
await tester.pump(const Duration(milliseconds: 10));
await tester.pump(const Duration(milliseconds: 50));
await tester.pumpAndSettle(const Duration(milliseconds: 122));
position.animateTo(-10000.0, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pump(const Duration(milliseconds: 10));
await tester.pump(const Duration(milliseconds: 10));
await tester.pump(const Duration(milliseconds: 50));
await tester.pumpAndSettle(const Duration(milliseconds: 122));
position.animateTo(10000.0, curve: Curves.linear, duration: const Duration(minutes: 1));
await tester.pump(const Duration(milliseconds: 10));
await tester.pump(const Duration(milliseconds: 10));
await tester.pump(const Duration(milliseconds: 50));
await tester.pumpAndSettle(const Duration(milliseconds: 122));
position.animateTo(-10000.0, curve: Curves.linear, duration: const Duration(seconds: 1));
await tester.pump(const Duration(milliseconds: 10));
await tester.pump(const Duration(milliseconds: 10));
await tester.pump(const Duration(milliseconds: 50));
await tester.pumpAndSettle(const Duration(milliseconds: 122));
position.animateTo(10000.0, curve: Curves.linear, duration: const Duration(seconds: 1));
await tester.pump(const Duration(milliseconds: 10));
await tester.pump(const Duration(milliseconds: 10));
await tester.pump(const Duration(milliseconds: 50));
await tester.pumpAndSettle(const Duration(milliseconds: 122));
});
testWidgets('Removing offscreen items above and rescrolling does not crash', (WidgetTester tester) async {
await tester.pumpWidget(new MaterialApp(
home: new CustomScrollView(
cacheExtent: 0.0,
slivers: <Widget>[
new SliverFixedExtentList(
itemExtent: 100.0,
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
return new Container(
color: Colors.blue,
child: new Text(index.toString()),
);
},
childCount: 30,
),
),
],
),
));
await tester.drag(find.text('5'), const Offset(0.0, -500.0));
await tester.pump();
// Screen is 600px high. Moved bottom item 500px up. It's now at the top.
expect(tester.getTopLeft(find.widgetWithText(DecoratedBox, '5')).dy, 0.0);
expect(tester.getBottomLeft(find.widgetWithText(DecoratedBox, '10')).dy, 600.0);
// Stop returning the first 3 items.
await tester.pumpWidget(new MaterialApp(
home: new CustomScrollView(
cacheExtent: 0.0,
slivers: <Widget>[
new SliverFixedExtentList(
itemExtent: 100.0,
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
if (index > 3) {
return new Container(
color: Colors.blue,
child: new Text(index.toString()),
);
}
return null;
},
childCount: 30,
),
),
],
),
));
await tester.drag(find.text('5'), const Offset(0.0, 400.0));
await tester.pump();
// Move up by 4 items, meaning item 1 would have been at the top but
// 0 through 3 no longer exist, so item 4, 3 items down, is the first one.
// Item 4 is also shifted to the top.
expect(tester.getTopLeft(find.widgetWithText(DecoratedBox, '4')).dy, 0.0);
// Because the screen is still 600px, item 9 is now visible at the bottom instead
// of what's supposed to be item 6 had we not re-shifted.
expect(tester.getBottomLeft(find.widgetWithText(DecoratedBox, '9')).dy, 600.0);
});
}