Commit 15bf91fb authored by Adam Barth's avatar Adam Barth Committed by GitHub

Add RTL support to scrolling (#11842)

This patch includes:

 * SliverPadding
 * ScrollView
 * PageView

Fixes #11764
Fixes #11836
parent 76f24515
......@@ -29,24 +29,49 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
///
/// The [padding] argument must not be null and must have non-negative insets.
RenderSliverPadding({
@required EdgeInsets padding,
@required EdgeInsetsGeometry padding,
TextDirection textDirection,
RenderSliver child,
}) : assert(padding != null),
assert(padding.isNonNegative),
_padding = padding {
_padding = padding,
_textDirection = textDirection {
this.child = child;
_applyUpdate();
}
// The resolved absolute insets.
EdgeInsets _resolvedPadding;
void _applyUpdate() {
final EdgeInsets resolvedPadding = padding.resolve(textDirection);
assert(resolvedPadding.isNonNegative);
if (_resolvedPadding != resolvedPadding) {
_resolvedPadding = resolvedPadding;
markNeedsLayout();
}
}
/// The amount to pad the child in each dimension.
EdgeInsets get padding => _padding;
EdgeInsets _padding;
set padding(EdgeInsets value) {
EdgeInsetsGeometry get padding => _padding;
EdgeInsetsGeometry _padding;
set padding(EdgeInsetsGeometry value) {
assert(value != null);
assert(value.isNonNegative);
assert(padding.isNonNegative);
if (_padding == value)
return;
_padding = value;
markNeedsLayout();
_applyUpdate();
}
/// The text direction with which to resolve [padding].
TextDirection get textDirection => _textDirection;
TextDirection _textDirection;
set textDirection(TextDirection value) {
if (_textDirection == value)
return;
_textDirection = value;
_applyUpdate();
}
/// The padding in the scroll direction on the side nearest the 0.0 scroll direction.
......@@ -59,13 +84,13 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
assert(constraints.growthDirection != null);
switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) {
case AxisDirection.up:
return padding.bottom;
return _resolvedPadding.bottom;
case AxisDirection.right:
return padding.left;
return _resolvedPadding.left;
case AxisDirection.down:
return padding.top;
return _resolvedPadding.top;
case AxisDirection.left:
return padding.right;
return _resolvedPadding.right;
}
return null;
}
......@@ -80,13 +105,13 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
assert(constraints.growthDirection != null);
switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) {
case AxisDirection.up:
return padding.top;
return _resolvedPadding.top;
case AxisDirection.right:
return padding.right;
return _resolvedPadding.right;
case AxisDirection.down:
return padding.bottom;
return _resolvedPadding.bottom;
case AxisDirection.left:
return padding.left;
return _resolvedPadding.left;
}
return null;
}
......@@ -100,7 +125,7 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
double get mainAxisPadding {
assert(constraints != null);
assert(constraints.axis != null);
return padding.along(constraints.axis);
return _resolvedPadding.along(constraints.axis);
}
/// The total padding in the cross-axis direction. (In other words, for a
......@@ -114,9 +139,9 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
assert(constraints.axis != null);
switch (constraints.axis) {
case Axis.horizontal:
return padding.vertical;
return _resolvedPadding.vertical;
case Axis.vertical:
return padding.horizontal;
return _resolvedPadding.horizontal;
}
return null;
}
......@@ -183,16 +208,16 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
assert(constraints.growthDirection != null);
switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) {
case AxisDirection.up:
childParentData.paintOffset = new Offset(padding.left, calculatePaintOffset(constraints, from: padding.bottom + childLayoutGeometry.scrollExtent, to: padding.bottom + childLayoutGeometry.scrollExtent + padding.top));
childParentData.paintOffset = new Offset(_resolvedPadding.left, calculatePaintOffset(constraints, from: _resolvedPadding.bottom + childLayoutGeometry.scrollExtent, to: _resolvedPadding.bottom + childLayoutGeometry.scrollExtent + _resolvedPadding.top));
break;
case AxisDirection.right:
childParentData.paintOffset = new Offset(calculatePaintOffset(constraints, from: 0.0, to: padding.left), padding.top);
childParentData.paintOffset = new Offset(calculatePaintOffset(constraints, from: 0.0, to: _resolvedPadding.left), _resolvedPadding.top);
break;
case AxisDirection.down:
childParentData.paintOffset = new Offset(padding.left, calculatePaintOffset(constraints, from: 0.0, to: padding.top));
childParentData.paintOffset = new Offset(_resolvedPadding.left, calculatePaintOffset(constraints, from: 0.0, to: _resolvedPadding.top));
break;
case AxisDirection.left:
childParentData.paintOffset = new Offset(calculatePaintOffset(constraints, from: padding.right + childLayoutGeometry.scrollExtent, to: padding.right + childLayoutGeometry.scrollExtent + padding.left), padding.top);
childParentData.paintOffset = new Offset(calculatePaintOffset(constraints, from: _resolvedPadding.right + childLayoutGeometry.scrollExtent, to: _resolvedPadding.right + childLayoutGeometry.scrollExtent + _resolvedPadding.left), _resolvedPadding.top);
break;
}
assert(childParentData.paintOffset != null);
......@@ -226,10 +251,10 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
switch (applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection)) {
case AxisDirection.up:
case AxisDirection.down:
return padding.left;
return _resolvedPadding.left;
case AxisDirection.left:
case AxisDirection.right:
return padding.top;
return _resolvedPadding.top;
}
return null;
}
......@@ -279,4 +304,11 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
return true;
});
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description);
description.add(new DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding));
description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
}
}
......@@ -2063,22 +2063,27 @@ class SliverPadding extends SingleChildRenderObjectWidget {
super(key: key, child: sliver);
/// The amount of space by which to inset the child sliver.
final EdgeInsets padding;
// TODO(ianh): RTL
final EdgeInsetsGeometry padding;
@override
RenderSliverPadding createRenderObject(BuildContext context) => new RenderSliverPadding(padding: padding);
RenderSliverPadding createRenderObject(BuildContext context) {
return new RenderSliverPadding(
padding: padding,
textDirection: Directionality.of(context),
);
}
@override
void updateRenderObject(BuildContext context, RenderSliverPadding renderObject) {
renderObject.padding = padding;
renderObject
..padding = padding
..textDirection = Directionality.of(context);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description);
description.add(new DiagnosticsProperty<EdgeInsets>('padding', padding));
description.add(new DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding));
}
}
......
......@@ -449,10 +449,17 @@ class _PageViewState extends State<PageView> {
}
AxisDirection _getDirection(BuildContext context) {
// TODO(abarth): Consider reading direction.
switch (widget.scrollDirection) {
case Axis.horizontal:
return widget.reverse ? AxisDirection.left : AxisDirection.right;
final TextDirection textDirection = Directionality.of(context);
assert(textDirection != null);
switch (textDirection) {
case TextDirection.rtl:
return widget.reverse ? AxisDirection.right : AxisDirection.left;
case TextDirection.ltr:
return widget.reverse ? AxisDirection.left : AxisDirection.right;
}
return null;
case Axis.vertical:
return widget.reverse ? AxisDirection.up : AxisDirection.down;
}
......
......@@ -170,13 +170,25 @@ abstract class ScrollView extends StatelessWidget {
/// Combines the [scrollDirection] with the [reverse] boolean to obtain the
/// concrete [AxisDirection].
///
/// In the future, this function will also consider the reading direction.
/// If the [scrollDirection] is [Axis.horizontal], the ambient
/// [Directionality] is also consided when selecting the concrete
/// [AxisDirection]. For example, if the ambient [Directionality] is
/// [TextDirection.rtl], then the non-reversed [AxisDirection] is
/// [AxisDirection.left] and the reversed [AxisDirection] is
/// [AxisDirection.right].
@protected
AxisDirection getDirection(BuildContext context) {
// TODO(abarth): Consider reading direction.
switch (scrollDirection) {
case Axis.horizontal:
return reverse ? AxisDirection.left : AxisDirection.right;
final TextDirection textDirection = Directionality.of(context);
assert(textDirection != null);
switch (textDirection) {
case TextDirection.rtl:
return reverse ? AxisDirection.right : AxisDirection.left;
case TextDirection.ltr:
return reverse ? AxisDirection.left : AxisDirection.right;
}
return null;
case Axis.vertical:
return reverse ? AxisDirection.up : AxisDirection.down;
}
......@@ -367,7 +379,7 @@ abstract class BoxScrollView extends ScrollView {
);
/// The amount of space by which to inset the children.
final EdgeInsets padding;
final EdgeInsetsGeometry padding;
@override
List<Widget> buildSlivers(BuildContext context) {
......@@ -384,7 +396,7 @@ abstract class BoxScrollView extends ScrollView {
@override
void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description);
description.add(new DiagnosticsProperty<EdgeInsets>('padding', padding, defaultValue: null));
description.add(new DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
}
}
......@@ -545,7 +557,7 @@ class ListView extends BoxScrollView {
bool primary,
ScrollPhysics physics,
bool shrinkWrap: false,
EdgeInsets padding,
EdgeInsetsGeometry padding,
this.itemExtent,
bool addAutomaticKeepAlives: true,
bool addRepaintBoundaries: true,
......@@ -597,7 +609,7 @@ class ListView extends BoxScrollView {
bool primary,
ScrollPhysics physics,
bool shrinkWrap: false,
EdgeInsets padding,
EdgeInsetsGeometry padding,
this.itemExtent,
@required IndexedWidgetBuilder itemBuilder,
int itemCount,
......@@ -631,7 +643,7 @@ class ListView extends BoxScrollView {
bool primary,
ScrollPhysics physics,
bool shrinkWrap: false,
EdgeInsets padding,
EdgeInsetsGeometry padding,
this.itemExtent,
@required this.childrenDelegate,
}) : assert(childrenDelegate != null),
......@@ -825,7 +837,7 @@ class GridView extends BoxScrollView {
bool primary,
ScrollPhysics physics,
bool shrinkWrap: false,
EdgeInsets padding,
EdgeInsetsGeometry padding,
@required this.gridDelegate,
bool addAutomaticKeepAlives: true,
bool addRepaintBoundaries: true,
......@@ -874,7 +886,7 @@ class GridView extends BoxScrollView {
bool primary,
ScrollPhysics physics,
bool shrinkWrap: false,
EdgeInsets padding,
EdgeInsetsGeometry padding,
@required this.gridDelegate,
@required IndexedWidgetBuilder itemBuilder,
int itemCount,
......@@ -913,7 +925,7 @@ class GridView extends BoxScrollView {
bool primary,
ScrollPhysics physics,
bool shrinkWrap: false,
EdgeInsets padding,
EdgeInsetsGeometry padding,
@required this.gridDelegate,
@required this.childrenDelegate,
}) : assert(gridDelegate != null),
......@@ -951,7 +963,7 @@ class GridView extends BoxScrollView {
bool primary,
ScrollPhysics physics,
bool shrinkWrap: false,
EdgeInsets padding,
EdgeInsetsGeometry padding,
@required int crossAxisCount,
double mainAxisSpacing: 0.0,
double crossAxisSpacing: 0.0,
......@@ -1002,7 +1014,7 @@ class GridView extends BoxScrollView {
bool primary,
ScrollPhysics physics,
bool shrinkWrap: false,
EdgeInsets padding,
EdgeInsetsGeometry padding,
@required double maxCrossAxisExtent,
double mainAxisSpacing: 0.0,
double crossAxisSpacing: 0.0,
......
......@@ -77,7 +77,7 @@ class SingleChildScrollView extends StatelessWidget {
final bool reverse;
/// The amount of space by which to inset the child.
final EdgeInsets padding;
final EdgeInsetsGeometry padding;
/// An object that can be used to control the position to which this scroll
/// view is scrolled.
......@@ -99,7 +99,7 @@ class SingleChildScrollView extends StatelessWidget {
/// On iOS, this identifies the scroll view that will scroll to top in
/// response to a tap in the status bar.
///
/// Defaults to true when `scrollDirection` is vertical and `controller` is
/// Defaults to true when [scrollDirection] is vertical and [controller] is
/// not specified.
final bool primary;
......@@ -115,10 +115,17 @@ class SingleChildScrollView extends StatelessWidget {
final Widget child;
AxisDirection _getDirection(BuildContext context) {
// TODO(abarth): Consider reading direction.
switch (scrollDirection) {
case Axis.horizontal:
return reverse ? AxisDirection.left : AxisDirection.right;
final TextDirection textDirection = Directionality.of(context);
assert(textDirection != null);
switch (textDirection) {
case TextDirection.rtl:
return reverse ? AxisDirection.right : AxisDirection.left;
case TextDirection.ltr:
return reverse ? AxisDirection.left : AxisDirection.right;
}
return null;
case Axis.vertical:
return reverse ? AxisDirection.up : AxisDirection.down;
}
......
......@@ -802,8 +802,9 @@ void main() {
length: 3,
);
await tester.pumpWidget(
new SizedBox.expand(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new SizedBox.expand(
child: new Center(
child: new SizedBox(
width: 400.0,
......@@ -819,7 +820,7 @@ void main() {
),
),
),
);
));
expect(tabController.index, 1);
......@@ -848,8 +849,9 @@ void main() {
length: 3,
);
await tester.pumpWidget(
new SizedBox.expand(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new SizedBox.expand(
child: new Center(
child: new SizedBox(
width: 400.0,
......@@ -866,7 +868,7 @@ void main() {
),
),
),
);
));
expect(tabController.index, 1);
......
......@@ -33,8 +33,9 @@ void main() {
testWidgets('Aspect ratio infinite width', (WidgetTester tester) async {
final Key childKey = new UniqueKey();
await tester.pumpWidget(
new Center(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: new AspectRatio(
......@@ -45,7 +46,7 @@ void main() {
)
)
)
);
));
final RenderBox box = tester.renderObject(find.byKey(childKey));
expect(box.size, equals(const Size(1200.0, 600.0)));
});
......
......@@ -42,14 +42,17 @@ Widget buildTest({ double startToEndThreshold }) {
);
}
return new Container(
padding: const EdgeInsets.all(10.0),
child: new ListView(
scrollDirection: scrollDirection,
itemExtent: itemExtent,
children: <int>[0, 1, 2, 3, 4]
.where((int i) => !dismissedItems.contains(i))
.map(buildDismissibleItem).toList(),
return new Directionality(
textDirection: TextDirection.ltr,
child: new Container(
padding: const EdgeInsets.all(10.0),
child: new ListView(
scrollDirection: scrollDirection,
itemExtent: itemExtent,
children: <int>[0, 1, 2, 3, 4]
.where((int i) => !dismissedItems.contains(i))
.map(buildDismissibleItem).toList(),
),
),
);
},
......
......@@ -11,15 +11,44 @@ import 'package:flutter/widgets.dart';
Finder findKey(int i) => find.byKey(new ValueKey<int>(i));
Widget buildSingleChildScrollView(Axis scrollDirection, { bool reverse: false }) {
return new Center(
child: new SizedBox(
width: 600.0,
height: 400.0,
child: new SingleChildScrollView(
scrollDirection: scrollDirection,
reverse: reverse,
child: new ListBody(
mainAxis: scrollDirection,
return new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 600.0,
height: 400.0,
child: new SingleChildScrollView(
scrollDirection: scrollDirection,
reverse: reverse,
child: new ListBody(
mainAxis: scrollDirection,
children: <Widget>[
new Container(key: const ValueKey<int>(0), width: 200.0, height: 200.0),
new Container(key: const ValueKey<int>(1), width: 200.0, height: 200.0),
new Container(key: const ValueKey<int>(2), width: 200.0, height: 200.0),
new Container(key: const ValueKey<int>(3), width: 200.0, height: 200.0),
new Container(key: const ValueKey<int>(4), width: 200.0, height: 200.0),
new Container(key: const ValueKey<int>(5), width: 200.0, height: 200.0),
new Container(key: const ValueKey<int>(6), width: 200.0, height: 200.0),
],
),
),
),
),
);
}
Widget buildListView(Axis scrollDirection, { bool reverse: false, bool shrinkWrap: false }) {
return new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 600.0,
height: 400.0,
child: new ListView(
scrollDirection: scrollDirection,
reverse: reverse,
shrinkWrap: shrinkWrap,
children: <Widget>[
new Container(key: const ValueKey<int>(0), width: 200.0, height: 200.0),
new Container(key: const ValueKey<int>(1), width: 200.0, height: 200.0),
......@@ -35,29 +64,6 @@ Widget buildSingleChildScrollView(Axis scrollDirection, { bool reverse: false })
);
}
Widget buildListView(Axis scrollDirection, { bool reverse: false, bool shrinkWrap: false }) {
return new Center(
child: new SizedBox(
width: 600.0,
height: 400.0,
child: new ListView(
scrollDirection: scrollDirection,
reverse: reverse,
shrinkWrap: shrinkWrap,
children: <Widget>[
new Container(key: const ValueKey<int>(0), width: 200.0, height: 200.0),
new Container(key: const ValueKey<int>(1), width: 200.0, height: 200.0),
new Container(key: const ValueKey<int>(2), width: 200.0, height: 200.0),
new Container(key: const ValueKey<int>(3), width: 200.0, height: 200.0),
new Container(key: const ValueKey<int>(4), width: 200.0, height: 200.0),
new Container(key: const ValueKey<int>(5), width: 200.0, height: 200.0),
new Container(key: const ValueKey<int>(6), width: 200.0, height: 200.0),
],
),
),
);
}
void main() {
group('SingleChildScollView', () {
......@@ -423,8 +429,9 @@ void main() {
await tester.pump();
}
await tester.pumpWidget(
new Center(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 600.0,
height: 400.0,
......@@ -454,7 +461,7 @@ void main() {
),
),
)
);
));
await prepare(321.0);
Scrollable.ensureVisible(findContext(0));
......
......@@ -126,8 +126,9 @@ void main() {
testWidgets('GridView large scroll jump', (WidgetTester tester) async {
final List<int> log = <int>[];
await tester.pumpWidget(
new GridView.extent(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new GridView.extent(
scrollDirection: Axis.horizontal,
maxCrossAxisExtent: 200.0,
childAspectRatio: 0.75,
......@@ -142,7 +143,7 @@ void main() {
);
}),
),
);
));
expect(tester.getSize(find.text('4')), equals(const Size(200.0 / 0.75, 200.0)));
......
......@@ -127,15 +127,18 @@ void main() {
);
};
FlipWidget buildWidget() {
return new FlipWidget(
left: new ListView.builder(
controller: new ScrollController(initialScrollOffset: 300.0),
itemBuilder: itemBuilder,
itemExtent: 200.0,
scrollDirection: Axis.horizontal
Widget buildWidget() {
return new Directionality(
textDirection: TextDirection.ltr,
child: new FlipWidget(
left: new ListView.builder(
controller: new ScrollController(initialScrollOffset: 300.0),
itemBuilder: itemBuilder,
itemExtent: 200.0,
scrollDirection: Axis.horizontal,
),
right: const Text('Not Today'),
),
right: const Text('Not Today')
);
}
......
......@@ -3,33 +3,37 @@
// found in the LICENSE file.
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
const List<int> items = const <int>[0, 1, 2, 3, 4, 5];
Widget buildFrame({ bool reverse: false }) {
return new Center(
child: new Container(
height: 50.0,
child: new ListView(
itemExtent: 290.0,
scrollDirection: Axis.horizontal,
reverse: reverse,
physics: const BouncingScrollPhysics(),
children: items.map((int item) {
return new Container(
child: new Text('$item')
);
}).toList(),
Widget buildFrame({ bool reverse: false, @required TextDirection textDirection }) {
return new Directionality(
textDirection: textDirection,
child: new Center(
child: new Container(
height: 50.0,
child: new ListView(
itemExtent: 290.0,
scrollDirection: Axis.horizontal,
reverse: reverse,
physics: const BouncingScrollPhysics(),
children: items.map((int item) {
return new Container(
child: new Text('$item')
);
}).toList(),
),
),
),
);
}
void main() {
testWidgets('Drag horizontally with scroll anchor at start', (WidgetTester tester) async {
await tester.pumpWidget(buildFrame());
testWidgets('Drag horizontally with scroll anchor at start (LTR)', (WidgetTester tester) async {
await tester.pumpWidget(buildFrame(textDirection: TextDirection.ltr));
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('1'), const Offset(-300.0, 0.0));
......@@ -117,7 +121,7 @@ void main() {
expect(find.text('5'), findsOneWidget);
await tester.pumpWidget(new Container());
await tester.pumpWidget(buildFrame(), const Duration(seconds: 1));
await tester.pumpWidget(buildFrame(textDirection: TextDirection.ltr), const Duration(seconds: 1));
await tester.drag(find.text('2'), const Offset(-280.0, 0.0));
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
......@@ -147,8 +151,8 @@ void main() {
expect(find.text('5'), findsNothing);
});
testWidgets('Drag horizontally with scroll anchor at end', (WidgetTester tester) async {
await tester.pumpWidget(buildFrame(reverse: true));
testWidgets('Drag horizontally with scroll anchor at end (LTR)', (WidgetTester tester) async {
await tester.pumpWidget(buildFrame(reverse: true, textDirection: TextDirection.ltr));
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
......@@ -246,4 +250,223 @@ void main() {
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsOneWidget);
});
testWidgets('Drag horizontally with scroll anchor at start (RTL)', (WidgetTester tester) async {
await tester.pumpWidget(buildFrame(textDirection: TextDirection.rtl));
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -70..220 = 2
// 220..510 = 1
// 510..800 = 0
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsOneWidget);
expect(find.text('2'), findsOneWidget);
expect(find.text('3'), findsNothing);
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
await tester.drag(find.text('0'), const Offset(300.0, 0.0));
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -80..210 = 3
// 230..520 = 2
// 520..810 = 1
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
expect(find.text('2'), findsOneWidget);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
// the center of item 3 is visible, so this works;
// if item 3 was a bit wider, such that its center was past the 800px mark, this would fail,
// because it wouldn't be hit tested when scrolling from its center, as drag() does.
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('2'), const Offset(290.0, 0.0));
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 4
// 280..570 = 3
// 570..860 = 2
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsOneWidget);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('2'), const Offset(0.0, 290.0));
await tester.pump(const Duration(seconds: 1));
// unchanged
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsOneWidget);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('3'), const Offset(290.0, 0.0));
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 5
// 280..570 = 4
// 570..860 = 3
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsNothing);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsOneWidget);
await tester.pump(const Duration(seconds: 1));
// at this point we can drag 60 pixels further before we hit the friction zone
// then, every pixel we drag is equivalent to half a pixel of movement
// to move item 3 entirely off screen therefore takes:
// 60 + (290-60)*2 = 520 pixels
// plus a couple more to be sure
await tester.drag(find.text('4'), const Offset(522.0, 0.0));
await tester.pump(); // just after release
// screen is 800px wide, and has the following items:
// 280..570 = 5
// 570..860 = 4
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsNothing);
expect(find.text('3'), findsNothing);
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsOneWidget);
await tester.pump(const Duration(seconds: 1)); // a second after release
// screen is 800px wide, and has the following items:
// 0..290 = 5
// 290..580 = 4
// 580..870 = 3
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsNothing);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsOneWidget);
});
testWidgets('Drag horizontally with scroll anchor at end (LTR)', (WidgetTester tester) async {
await tester.pumpWidget(buildFrame(reverse: true, textDirection: TextDirection.rtl));
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('1'), const Offset(-300.0, 0.0));
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 1
// 280..570 = 2
// 570..860 = 3
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
expect(find.text('2'), findsOneWidget);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
// the center of item 3 is visible, so this works;
// if item 3 was a bit wider, such that its center was past the 800px mark, this would fail,
// because it wouldn't be hit tested when scrolling from its center, as drag() does.
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('3'), const Offset(-290.0, 0.0));
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 2
// 280..570 = 3
// 570..860 = 4
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsOneWidget);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('3'), const Offset(0.0, -290.0));
await tester.pump(const Duration(seconds: 1));
// unchanged
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsOneWidget);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('3'), const Offset(-290.0, 0.0));
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 3
// 280..570 = 4
// 570..860 = 5
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsNothing);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsOneWidget);
await tester.pump(const Duration(seconds: 1));
// at this point we can drag 60 pixels further before we hit the friction zone
// then, every pixel we drag is equivalent to half a pixel of movement
// to move item 3 entirely off screen therefore takes:
// 60 + (290-60)*2 = 520 pixels
// plus a couple more to be sure
await tester.drag(find.text('3'), const Offset(-522.0, 0.0));
await tester.pump(); // just after release
// screen is 800px wide, and has the following items:
// -11..279 = 4
// 279..569 = 5
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsNothing);
expect(find.text('3'), findsNothing);
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsOneWidget);
await tester.pump(const Duration(seconds: 1)); // a second after release
// screen is 800px wide, and has the following items:
// -70..220 = 3
// 220..510 = 4
// 510..800 = 5
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsNothing);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsOneWidget);
await tester.pumpWidget(new Container());
await tester.pumpWidget(buildFrame(reverse: true, textDirection: TextDirection.rtl), const Duration(seconds: 1));
await tester.drag(find.text('2'), const Offset(-280.0, 0.0));
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -280..10 = 0
// 10..300 = 1
// 300..590 = 2
// 590..880 = 3
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsOneWidget);
expect(find.text('2'), findsOneWidget);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('2'), const Offset(-290.0, 0.0));
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -280..10 = 1
// 10..300 = 2
// 300..590 = 3
// 590..880 = 4
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
expect(find.text('2'), findsOneWidget);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsNothing);
});
}
......@@ -117,13 +117,16 @@ void main() {
};
Widget builder() {
return new FlipWidget(
left: new ListView.builder(
scrollDirection: Axis.horizontal,
controller: new ScrollController(initialScrollOffset: 300.0),
itemBuilder: itemBuilder,
return new Directionality(
textDirection: TextDirection.ltr,
child: new FlipWidget(
left: new ListView.builder(
scrollDirection: Axis.horizontal,
controller: new ScrollController(initialScrollOffset: 300.0),
itemBuilder: itemBuilder,
),
right: const Text('Not Today'),
),
right: const Text('Not Today'),
);
}
......
......@@ -263,15 +263,16 @@ void main() {
expect(controller.mostRecentlyUpdatedPosition, isNull);
expect(controller.initialScrollOffset, 0.0);
await tester.pumpWidget(
new PageView(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new PageView(
children: <Widget>[
buildTest(controller: controller, title: 'Page0'),
buildTest(controller: controller, title: 'Page1'),
buildTest(controller: controller, title: 'Page2'),
],
),
);
));
// Initially Page0 is visible and Page0's appbar is fully expanded (height = 200.0).
expect(find.text('Page0'), findsOneWidget);
......
......@@ -180,15 +180,16 @@ void main() {
});
testWidgets('Overscroll horizontally', (WidgetTester tester) async {
await tester.pumpWidget(
new CustomScrollView(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new CustomScrollView(
scrollDirection: Axis.horizontal,
physics: const AlwaysScrollableScrollPhysics(),
slivers: <Widget>[
const SliverToBoxAdapter(child: const SizedBox(height: 20.0)),
],
),
);
));
final RenderObject painter = tester.renderObject(find.byType(CustomPaint));
await slowDrag(tester, const Offset(200.0, 200.0), const Offset(5.0, 0.0));
expect(painter, paints..rotate(angle: math.PI / 2.0)..circle()..saveRestore());
......@@ -202,8 +203,9 @@ void main() {
});
testWidgets('Nested overscrolls do not throw exceptions', (WidgetTester tester) async {
await tester.pumpWidget(
new PageView(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new PageView(
children: <Widget>[
new ListView(
children: <Widget>[
......@@ -216,7 +218,7 @@ void main() {
),
],
),
);
));
await tester.dragFrom(const Offset(100.0, 100.0), const Offset(0.0, 2000.0));
await tester.pumpAndSettle();
......@@ -225,8 +227,9 @@ void main() {
testWidgets('Changing settings', (WidgetTester tester) async {
RenderObject painter;
await tester.pumpWidget(
new ScrollConfiguration(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new ScrollConfiguration(
behavior: new TestScrollBehavior1(),
child: new CustomScrollView(
scrollDirection: Axis.horizontal,
......@@ -237,15 +240,16 @@ void main() {
],
),
),
);
));
painter = tester.renderObject(find.byType(CustomPaint));
await slowDrag(tester, const Offset(200.0, 200.0), const Offset(5.0, 0.0));
expect(painter, paints..rotate(angle: math.PI / 2.0)..circle(color: const Color(0x0A00FF00)));
expect(painter, isNot(paints..circle()..circle()));
await tester.pumpAndSettle(const Duration(seconds: 1));
await tester.pumpWidget(
new ScrollConfiguration(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new ScrollConfiguration(
behavior: new TestScrollBehavior2(),
child: new CustomScrollView(
scrollDirection: Axis.horizontal,
......@@ -255,7 +259,7 @@ void main() {
],
),
),
);
));
painter = tester.renderObject(find.byType(CustomPaint));
await slowDrag(tester, const Offset(200.0, 200.0), const Offset(5.0, 0.0));
expect(painter, paints..rotate(angle: math.PI / 2.0)..circle(color: const Color(0x0A0000FF))..saveRestore());
......
......@@ -14,19 +14,22 @@ void main() {
testWidgets('PageView control test', (WidgetTester tester) async {
final List<String> log = <String>[];
await tester.pumpWidget(new PageView(
children: kStates.map<Widget>((String state) {
return new GestureDetector(
onTap: () {
log.add(state);
},
child: new Container(
height: 200.0,
color: const Color(0xFF0000FF),
child: new Text(state),
),
);
}).toList(),
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new PageView(
children: kStates.map<Widget>((String state) {
return new GestureDetector(
onTap: () {
log.add(state);
},
child: new Container(
height: 200.0,
color: const Color(0xFF0000FF),
child: new Text(state),
),
);
}).toList(),
),
));
await tester.tap(find.text('Alabama'));
......@@ -111,13 +114,16 @@ void main() {
testWidgets('PageController control test', (WidgetTester tester) async {
final PageController controller = new PageController(initialPage: 4);
await tester.pumpWidget(new Center(
child: new SizedBox(
width: 600.0,
height: 400.0,
child: new PageView(
controller: controller,
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 600.0,
height: 400.0,
child: new PageView(
controller: controller,
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
),
),
));
......@@ -129,13 +135,16 @@ void main() {
expect(find.text('Colorado'), findsOneWidget);
await tester.pumpWidget(new Center(
child: new SizedBox(
width: 300.0,
height: 400.0,
child: new PageView(
controller: controller,
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 300.0,
height: 400.0,
child: new PageView(
controller: controller,
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
),
),
));
......@@ -149,13 +158,16 @@ void main() {
});
testWidgets('PageController page stability', (WidgetTester tester) async {
await tester.pumpWidget(new Center(
child: new SizedBox(
width: 600.0,
height: 400.0,
child: new PageView(
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 600.0,
height: 400.0,
child: new PageView(
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
)
),
));
......@@ -166,24 +178,30 @@ void main() {
expect(find.text('Arizona'), findsOneWidget);
await tester.pumpWidget(new Center(
child: new SizedBox(
width: 250.0,
height: 100.0,
child: new PageView(
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 250.0,
height: 100.0,
child: new PageView(
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
),
),
));
expect(find.text('Arizona'), findsOneWidget);
await tester.pumpWidget(new Center(
child: new SizedBox(
width: 450.0,
height: 400.0,
child: new PageView(
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 450.0,
height: 400.0,
child: new PageView(
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
),
),
));
......@@ -192,24 +210,30 @@ void main() {
});
testWidgets('PageView in zero-size container', (WidgetTester tester) async {
await tester.pumpWidget(new Center(
child: new SizedBox(
width: 0.0,
height: 0.0,
child: new PageView(
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 0.0,
height: 0.0,
child: new PageView(
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
),
),
));
expect(find.text('Alabama'), findsOneWidget);
await tester.pumpWidget(new Center(
child: new SizedBox(
width: 200.0,
height: 200.0,
child: new PageView(
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 200.0,
height: 200.0,
child: new PageView(
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
),
),
));
......@@ -219,9 +243,12 @@ void main() {
testWidgets('Page changes at halfway point', (WidgetTester tester) async {
final List<int> log = <int>[];
await tester.pumpWidget(new PageView(
onPageChanged: log.add,
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new PageView(
onPageChanged: log.add,
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
));
expect(log, isEmpty);
......@@ -268,18 +295,21 @@ void main() {
PageController controller = new PageController(viewportFraction: 7/8);
Widget build(PageController controller) {
return new PageView.builder(
controller: controller,
itemCount: kStates.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
height: 200.0,
color: index % 2 == 0
? const Color(0xFF0000FF)
: const Color(0xFF00FF00),
child: new Text(kStates[index]),
);
},
return new Directionality(
textDirection: TextDirection.ltr,
child: new PageView.builder(
controller: controller,
itemCount: kStates.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
height: 200.0,
color: index % 2 == 0
? const Color(0xFF0000FF)
: const Color(0xFF00FF00),
child: new Text(kStates[index]),
);
},
),
);
}
......@@ -308,18 +338,21 @@ void main() {
final PageController controller = new PageController(viewportFraction: 1/8);
Widget build(PageController controller) {
return new PageView.builder(
controller: controller,
itemCount: kStates.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
height: 200.0,
color: index % 2 == 0
? const Color(0xFF0000FF)
: const Color(0xFF00FF00),
child: new Text(kStates[index]),
);
},
return new Directionality(
textDirection: TextDirection.ltr,
child: new PageView.builder(
controller: controller,
itemCount: kStates.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
height: 200.0,
color: index % 2 == 0
? const Color(0xFF0000FF)
: const Color(0xFF00FF00),
child: new Text(kStates[index]),
);
},
),
);
}
......@@ -350,18 +383,21 @@ void main() {
new PageController(viewportFraction: 5/4);
Widget build(PageController controller) {
return new PageView.builder(
controller: controller,
itemCount: kStates.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
height: 200.0,
color: index % 2 == 0
? const Color(0xFF0000FF)
: const Color(0xFF00FF00),
child: new Text(kStates[index]),
);
},
return new Directionality(
textDirection: TextDirection.ltr,
child: new PageView.builder(
controller: controller,
itemCount: kStates.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
height: 200.0,
color: index % 2 == 0
? const Color(0xFF0000FF)
: const Color(0xFF00FF00),
child: new Text(kStates[index]),
);
},
),
);
}
......@@ -383,13 +419,16 @@ void main() {
);
int changeIndex = 0;
Widget build() {
return new PageView(
children:
kStates.map<Widget>((String state) => new Text(state)).toList(),
controller: controller,
onPageChanged: (int page) {
changeIndex = page;
},
return new Directionality(
textDirection: TextDirection.ltr,
child: new PageView(
children:
kStates.map<Widget>((String state) => new Text(state)).toList(),
controller: controller,
onPageChanged: (int page) {
changeIndex = page;
},
),
);
}
......@@ -405,8 +444,9 @@ void main() {
(WidgetTester tester) async {
final PageController controller = new PageController();
final PageStorageBucket bucket = new PageStorageBucket();
await tester.pumpWidget(
new PageStorage(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new PageStorage(
bucket: bucket,
child: new PageView(
key: const PageStorageKey<String>('PageView'),
......@@ -418,7 +458,7 @@ void main() {
],
),
),
);
));
expect(controller.page, 0);
controller.jumpToPage(2);
expect(await tester.pumpAndSettle(const Duration(minutes: 1)), 1);
......@@ -430,8 +470,9 @@ void main() {
),
);
expect(() => controller.page, throwsAssertionError);
await tester.pumpWidget(
new PageStorage(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new PageStorage(
bucket: bucket,
child: new PageView(
key: const PageStorageKey<String>('PageView'),
......@@ -443,12 +484,13 @@ void main() {
],
),
),
);
));
expect(controller.page, 2);
final PageController controller2 = new PageController(keepPage: false);
await tester.pumpWidget(
new PageStorage(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new PageStorage(
bucket: bucket,
child: new PageView(
key: const PageStorageKey<String>('Check it again against your list and see consistency!'),
......@@ -460,7 +502,7 @@ void main() {
],
),
),
);
));
expect(controller2.page, 0);
});
}
......@@ -5,6 +5,7 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
Size pageSize = const Size(600.0, 300.0);
const List<int> defaultPages = const <int>[0, 1, 2, 3, 4, 5];
......@@ -22,7 +23,8 @@ Widget buildPage(int page) {
Widget buildFrame({
bool reverse: false,
List<int> pages: defaultPages
List<int> pages: defaultPages,
@required TextDirection textDirection,
}) {
final PageView child = new PageView(
scrollDirection: Axis.horizontal,
......@@ -33,9 +35,12 @@ Widget buildFrame({
// The test framework forces the frame to be 800x600, so we need to create
// an outer container where we can change the size.
return new Center(
child: new Container(
width: pageSize.width, height: pageSize.height, child: child,
return new Directionality(
textDirection: textDirection,
child: new Center(
child: new Container(
width: pageSize.width, height: pageSize.height, child: child,
),
),
);
}
......@@ -58,12 +63,15 @@ Future<Null> pageRight(WidgetTester tester) {
void main() {
testWidgets('PageView default control', (WidgetTester tester) async {
await tester.pumpWidget(new Center(child: new PageView()));
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(child: new PageView())
));
});
testWidgets('PageView control test', (WidgetTester tester) async {
testWidgets('PageView control test (LTR)', (WidgetTester tester) async {
currentPage = null;
await tester.pumpWidget(buildFrame());
await tester.pumpWidget(buildFrame(textDirection: TextDirection.ltr));
expect(currentPage, isNull);
await pageLeft(tester);
expect(currentPage, equals(1));
......@@ -89,9 +97,9 @@ void main() {
expect(currentPage, equals(0));
});
testWidgets('PageView with reverse', (WidgetTester tester) async {
testWidgets('PageView with reverse (LTR)', (WidgetTester tester) async {
currentPage = null;
await tester.pumpWidget(buildFrame(reverse: true));
await tester.pumpWidget(buildFrame(reverse: true, textDirection: TextDirection.ltr));
await pageRight(tester);
expect(currentPage, equals(1));
......@@ -122,4 +130,66 @@ void main() {
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
});
testWidgets('PageView control test (RTL)', (WidgetTester tester) async {
currentPage = null;
await tester.pumpWidget(buildFrame(textDirection: TextDirection.rtl));
await pageRight(tester);
expect(currentPage, equals(1));
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
expect(find.text('2'), findsNothing);
expect(find.text('3'), findsNothing);
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
await pageLeft(tester);
expect(currentPage, equals(0));
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsNothing);
expect(find.text('3'), findsNothing);
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
await pageLeft(tester);
expect(currentPage, equals(0));
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsNothing);
expect(find.text('3'), findsNothing);
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
});
testWidgets('PageView with reverse (RTL)', (WidgetTester tester) async {
currentPage = null;
await tester.pumpWidget(buildFrame(reverse: true, textDirection: TextDirection.rtl));
expect(currentPage, isNull);
await pageLeft(tester);
expect(currentPage, equals(1));
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
expect(find.text('2'), findsNothing);
expect(find.text('3'), findsNothing);
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
await pageRight(tester);
expect(currentPage, equals(0));
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsNothing);
expect(find.text('3'), findsNothing);
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
await pageRight(tester);
expect(currentPage, equals(0));
});
}
......@@ -11,20 +11,23 @@ const List<int> items = const <int>[0, 1, 2, 3, 4, 5];
void main() {
testWidgets('Tap item after scroll - horizontal', (WidgetTester tester) async {
final List<int> tapped = <int>[];
await tester.pumpWidget(new Center(
child: new Container(
height: 50.0,
child: new ListView(
itemExtent: 290.0,
scrollDirection: Axis.horizontal,
children: items.map((int item) {
return new Container(
child: new GestureDetector(
onTap: () { tapped.add(item); },
child: new Text('$item'),
),
);
}).toList(),
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new Container(
height: 50.0,
child: new ListView(
itemExtent: 290.0,
scrollDirection: Axis.horizontal,
children: items.map((int item) {
return new Container(
child: new GestureDetector(
onTap: () { tapped.add(item); },
child: new Text('$item'),
),
);
}).toList(),
),
),
),
));
......
......@@ -22,17 +22,20 @@ class TestItem extends StatelessWidget {
}
Widget buildFrame({ int count, double width, double height, Axis scrollDirection }) {
return new CustomScrollView(
scrollDirection: scrollDirection ?? Axis.vertical,
slivers: <Widget>[
new SliverPrototypeExtentList(
prototypeItem: new TestItem(item: -1, width: width, height: height),
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) => new TestItem(item: index),
childCount: count,
return new Directionality(
textDirection: TextDirection.ltr,
child: new CustomScrollView(
scrollDirection: scrollDirection ?? Axis.vertical,
slivers: <Widget>[
new SliverPrototypeExtentList(
prototypeItem: new TestItem(item: -1, width: width, height: height),
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) => new TestItem(item: index),
childCount: count,
),
),
),
],
],
),
);
}
......
......@@ -6,8 +6,8 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
Future<Null> test(WidgetTester tester, double offset, EdgeInsets padding, AxisDirection axisDirection) {
return tester.pumpWidget(new Viewport(
Future<Null> test(WidgetTester tester, double offset, EdgeInsetsGeometry padding, AxisDirection axisDirection, { TextDirection textDirection }) {
Widget widget = new Viewport(
offset: new ViewportOffset.fixed(offset),
axisDirection: axisDirection,
slivers: <Widget>[
......@@ -18,7 +18,14 @@ Future<Null> test(WidgetTester tester, double offset, EdgeInsets padding, AxisDi
),
const SliverToBoxAdapter(child: const SizedBox(width: 400.0, height: 400.0, child: const Text('after'))),
],
));
);
if (textDirection != null) {
widget = new Directionality(
textDirection: textDirection,
child: widget,
);
}
return tester.pumpWidget(widget);
}
void verify(WidgetTester tester, List<Rect> answerKey) {
......@@ -33,7 +40,7 @@ void verify(WidgetTester tester, List<Rect> answerKey) {
}
void main() {
testWidgets('Viewport+SliverPadding basic test', (WidgetTester tester) async {
testWidgets('Viewport+SliverPadding basic test (VISUAL)', (WidgetTester tester) async {
final EdgeInsets padding = const EdgeInsets.fromLTRB(25.0, 20.0, 15.0, 35.0);
await test(tester, 0.0, padding, AxisDirection.down);
expect(tester.renderObject<RenderBox>(find.byType(Viewport)).size, equals(const Size(800.0, 600.0)));
......@@ -72,6 +79,84 @@ void main() {
]);
});
testWidgets('Viewport+SliverPadding basic test (LTR)', (WidgetTester tester) async {
final EdgeInsetsDirectional padding = const EdgeInsetsDirectional.fromSTEB(25.0, 20.0, 15.0, 35.0);
await test(tester, 0.0, padding, AxisDirection.down, textDirection: TextDirection.ltr);
expect(tester.renderObject<RenderBox>(find.byType(Viewport)).size, equals(const Size(800.0, 600.0)));
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, 0.0, 800.0, 400.0),
new Rect.fromLTWH(25.0, 420.0, 760.0, 400.0),
new Rect.fromLTWH(0.0, 600.0, 800.0, 400.0),
]);
await test(tester, 200.0, padding, AxisDirection.down, textDirection: TextDirection.ltr);
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, -200.0, 800.0, 400.0),
new Rect.fromLTWH(25.0, 220.0, 760.0, 400.0),
new Rect.fromLTWH(0.0, 600.0, 800.0, 400.0),
]);
await test(tester, 390.0, padding, AxisDirection.down, textDirection: TextDirection.ltr);
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, -390.0, 800.0, 400.0),
new Rect.fromLTWH(25.0, 30.0, 760.0, 400.0),
new Rect.fromLTWH(0.0, 465.0, 800.0, 400.0),
]);
await test(tester, 490.0, padding, AxisDirection.down, textDirection: TextDirection.ltr);
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, -490.0, 800.0, 400.0),
new Rect.fromLTWH(25.0, -70.0, 760.0, 400.0),
new Rect.fromLTWH(0.0, 365.0, 800.0, 400.0),
]);
await test(tester, 10000.0, padding, AxisDirection.down, textDirection: TextDirection.ltr);
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, -10000.0, 800.0, 400.0),
new Rect.fromLTWH(25.0, -9580.0, 760.0, 400.0),
new Rect.fromLTWH(0.0, -9145.0, 800.0, 400.0),
]);
});
testWidgets('Viewport+SliverPadding basic test (RTL)', (WidgetTester tester) async {
final EdgeInsetsDirectional padding = const EdgeInsetsDirectional.fromSTEB(25.0, 20.0, 15.0, 35.0);
await test(tester, 0.0, padding, AxisDirection.down, textDirection: TextDirection.rtl);
expect(tester.renderObject<RenderBox>(find.byType(Viewport)).size, equals(const Size(800.0, 600.0)));
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, 0.0, 800.0, 400.0),
new Rect.fromLTWH(15.0, 420.0, 760.0, 400.0),
new Rect.fromLTWH(0.0, 600.0, 800.0, 400.0),
]);
await test(tester, 200.0, padding, AxisDirection.down, textDirection: TextDirection.rtl);
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, -200.0, 800.0, 400.0),
new Rect.fromLTWH(15.0, 220.0, 760.0, 400.0),
new Rect.fromLTWH(0.0, 600.0, 800.0, 400.0),
]);
await test(tester, 390.0, padding, AxisDirection.down, textDirection: TextDirection.rtl);
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, -390.0, 800.0, 400.0),
new Rect.fromLTWH(15.0, 30.0, 760.0, 400.0),
new Rect.fromLTWH(0.0, 465.0, 800.0, 400.0),
]);
await test(tester, 490.0, padding, AxisDirection.down, textDirection: TextDirection.rtl);
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, -490.0, 800.0, 400.0),
new Rect.fromLTWH(15.0, -70.0, 760.0, 400.0),
new Rect.fromLTWH(0.0, 365.0, 800.0, 400.0),
]);
await test(tester, 10000.0, padding, AxisDirection.down, textDirection: TextDirection.rtl);
verify(tester, <Rect>[
new Rect.fromLTWH(0.0, -10000.0, 800.0, 400.0),
new Rect.fromLTWH(15.0, -9580.0, 760.0, 400.0),
new Rect.fromLTWH(0.0, -9145.0, 800.0, 400.0),
]);
});
testWidgets('Viewport+SliverPadding hit testing', (WidgetTester tester) async {
final EdgeInsets padding = const EdgeInsets.all(30.0);
await test(tester, 350.0, padding, AxisDirection.down);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment