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:
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:
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:
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,7 +42,9 @@ Widget buildTest({ double startToEndThreshold }) {
);
}
return new Container(
return new Directionality(
textDirection: TextDirection.ltr,
child: new Container(
padding: const EdgeInsets.all(10.0),
child: new ListView(
scrollDirection: scrollDirection,
......@@ -51,6 +53,7 @@ Widget buildTest({ double startToEndThreshold }) {
.where((int i) => !dismissedItems.contains(i))
.map(buildDismissibleItem).toList(),
),
),
);
},
);
......
......@@ -11,7 +11,9 @@ 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(
return new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 600.0,
height: 400.0,
......@@ -32,11 +34,14 @@ Widget buildSingleChildScrollView(Axis scrollDirection, { bool reverse: false })
),
),
),
),
);
}
Widget buildListView(Axis scrollDirection, { bool reverse: false, bool shrinkWrap: false }) {
return new Center(
return new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 600.0,
height: 400.0,
......@@ -55,6 +60,7 @@ Widget buildListView(Axis scrollDirection, { bool reverse: false, bool shrinkWra
],
),
),
),
);
}
......@@ -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(
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
scrollDirection: Axis.horizontal,
),
right: const Text('Not Today'),
),
right: const Text('Not Today')
);
}
......
......@@ -117,13 +117,16 @@ void main() {
};
Widget builder() {
return new FlipWidget(
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'),
),
);
}
......
......@@ -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,7 +14,9 @@ void main() {
testWidgets('PageView control test', (WidgetTester tester) async {
final List<String> log = <String>[];
await tester.pumpWidget(new PageView(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new PageView(
children: kStates.map<Widget>((String state) {
return new GestureDetector(
onTap: () {
......@@ -27,6 +29,7 @@ void main() {
),
);
}).toList(),
),
));
await tester.tap(find.text('Alabama'));
......@@ -111,7 +114,9 @@ void main() {
testWidgets('PageController control test', (WidgetTester tester) async {
final PageController controller = new PageController(initialPage: 4);
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,
......@@ -120,6 +125,7 @@ void main() {
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
),
),
));
expect(find.text('California'), findsOneWidget);
......@@ -129,7 +135,9 @@ void main() {
expect(find.text('Colorado'), findsOneWidget);
await tester.pumpWidget(new Center(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 300.0,
height: 400.0,
......@@ -138,6 +146,7 @@ void main() {
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
),
),
));
expect(find.text('Colorado'), findsOneWidget);
......@@ -149,13 +158,16 @@ void main() {
});
testWidgets('PageController page stability', (WidgetTester tester) async {
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,
child: new PageView(
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
)
),
));
......@@ -166,7 +178,9 @@ void main() {
expect(find.text('Arizona'), findsOneWidget);
await tester.pumpWidget(new Center(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 250.0,
height: 100.0,
......@@ -174,11 +188,14 @@ void main() {
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
),
),
));
expect(find.text('Arizona'), findsOneWidget);
await tester.pumpWidget(new Center(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 450.0,
height: 400.0,
......@@ -186,13 +203,16 @@ void main() {
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
),
),
));
expect(find.text('Arizona'), findsOneWidget);
});
testWidgets('PageView in zero-size container', (WidgetTester tester) async {
await tester.pumpWidget(new Center(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 0.0,
height: 0.0,
......@@ -200,11 +220,14 @@ void main() {
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
),
),
));
expect(find.text('Alabama'), findsOneWidget);
await tester.pumpWidget(new Center(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new SizedBox(
width: 200.0,
height: 200.0,
......@@ -212,6 +235,7 @@ void main() {
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
),
),
),
));
expect(find.text('Alabama'), findsOneWidget);
......@@ -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(
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,7 +295,9 @@ void main() {
PageController controller = new PageController(viewportFraction: 7/8);
Widget build(PageController controller) {
return new PageView.builder(
return new Directionality(
textDirection: TextDirection.ltr,
child: new PageView.builder(
controller: controller,
itemCount: kStates.length,
itemBuilder: (BuildContext context, int index) {
......@@ -280,6 +309,7 @@ void main() {
child: new Text(kStates[index]),
);
},
),
);
}
......@@ -308,7 +338,9 @@ void main() {
final PageController controller = new PageController(viewportFraction: 1/8);
Widget build(PageController controller) {
return new PageView.builder(
return new Directionality(
textDirection: TextDirection.ltr,
child: new PageView.builder(
controller: controller,
itemCount: kStates.length,
itemBuilder: (BuildContext context, int index) {
......@@ -320,6 +352,7 @@ void main() {
child: new Text(kStates[index]),
);
},
),
);
}
......@@ -350,7 +383,9 @@ void main() {
new PageController(viewportFraction: 5/4);
Widget build(PageController controller) {
return new PageView.builder(
return new Directionality(
textDirection: TextDirection.ltr,
child: new PageView.builder(
controller: controller,
itemCount: kStates.length,
itemBuilder: (BuildContext context, int index) {
......@@ -362,6 +397,7 @@ void main() {
child: new Text(kStates[index]),
);
},
),
);
}
......@@ -383,13 +419,16 @@ void main() {
);
int changeIndex = 0;
Widget build() {
return new PageView(
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,10 +35,13 @@ 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(
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,7 +11,9 @@ 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(
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new Container(
height: 50.0,
child: new ListView(
......@@ -27,6 +29,7 @@ void main() {
}).toList(),
),
),
),
));
await tester.drag(find.text('2'), const Offset(-280.0, 0.0));
await tester.pump(const Duration(seconds: 1));
......
......@@ -22,7 +22,9 @@ class TestItem extends StatelessWidget {
}
Widget buildFrame({ int count, double width, double height, Axis scrollDirection }) {
return new CustomScrollView(
return new Directionality(
textDirection: TextDirection.ltr,
child: new CustomScrollView(
scrollDirection: scrollDirection ?? Axis.vertical,
slivers: <Widget>[
new SliverPrototypeExtentList(
......@@ -33,6 +35,7 @@ Widget buildFrame({ int count, double width, double height, Axis scrollDirection
),
),
],
),
);
}
......
......@@ -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