Commit 559621ca authored by Adam Barth's avatar Adam Barth Committed by GitHub

Add SingleChildScrollView (#7620)

This widget is a replacement for ScrollableViewport that uses the new
Scrollable2 machinery. The widget is not based on Slivers but does use the new
scroll behavior classes.
parent f683abd7
......@@ -334,7 +334,7 @@ class _ExpansionPanelsDemoState extends State<ExpasionPanelsDemo> {
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text('Expansion panels')),
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new Container(
margin: const EdgeInsets.all(24.0),
child: new ExpansionPanelList(
......
......@@ -132,7 +132,7 @@ class FullScreenCodeDialogState extends State<FullScreenCodeDialog> {
child: new CircularProgressIndicator()
);
} else {
body = new ScrollableViewport(
body = new SingleChildScrollView(
child: new Padding(
padding: new EdgeInsets.all(16.0),
child: new RichText(
......
......@@ -400,7 +400,7 @@ class PaginatedDataTableState extends State<PaginatedDataTable> {
)
)
),
new ScrollableViewport(
new SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: new DataTable(
key: _tableKey,
......
......@@ -239,21 +239,6 @@ class SliverConstraints extends Constraints {
/// width of the viewport.
final double crossAxisExtent;
Offset get scrollOffsetAsOffset {
assert(axisDirection != null);
switch (axisDirection) {
case AxisDirection.up:
return new Offset(0.0, -scrollOffset);
case AxisDirection.down:
return new Offset(0.0, scrollOffset);
case AxisDirection.left:
return new Offset(-scrollOffset, 0.0);
case AxisDirection.right:
return new Offset(scrollOffset, 0.0);
}
return null;
}
Axis get axis => axisDirectionToAxis(axisDirection);
/// Return what the [growthDirection] would be if the [axisDirection] was
......@@ -1037,6 +1022,13 @@ abstract class ViewportOffset extends ChangeNotifier {
ViewportOffset();
factory ViewportOffset.fixed(double value) = _FixedViewportOffset;
factory ViewportOffset.zero() = _FixedViewportOffset.zero;
/// The number of pixels to offset the children in the opposite of the axis direction.
///
/// For example, if the axis direction is down, then the pixel value
/// represents the number of logical pixels to move the children _up_ the
/// screen. Similarly, if the axis direction is left, then the pixels value
/// represents the number of logical pixesl to move the children to _right_.
double get pixels;
/// Called when the viewport's extents are established.
......
......@@ -20,6 +20,7 @@ import 'scroll_simulation.dart';
import 'notification_listener.dart';
import 'scroll_notification.dart';
import 'scrollable.dart';
import 'viewport.dart';
/// Scrolling logic delegate for lists and other unremarkable scrollable
/// viewports.
......@@ -77,6 +78,25 @@ class ViewportScrollBehavior extends ScrollBehavior2 {
return null;
}
@override
Widget createViewport({
Key key,
AxisDirection axisDirection: AxisDirection.down,
double anchor: 0.0,
ViewportOffset offset,
Key center,
List<Widget> children: const <Widget>[],
}) {
return new Viewport2(
key: key,
axisDirection: axisDirection,
anchor: anchor,
offset: offset,
center: center,
children: children,
);
}
@override
ScrollPosition createScrollPosition(BuildContext context, Scrollable2State state, ScrollPosition oldPosition) {
switch (getPlatform(context)) {
......
......@@ -350,6 +350,15 @@ abstract class ScrollBehavior2 {
Widget wrap(BuildContext context, Widget child, AxisDirection axisDirection);
Widget createViewport({
Key key,
AxisDirection axisDirection: AxisDirection.down,
double anchor: 0.0,
ViewportOffset offset,
Key center,
List<Widget> children: const <Widget>[],
});
/// Returns a new instance of the ScrollPosition class that this
/// ScrollBehavior2 subclass creates.
///
......@@ -380,6 +389,48 @@ abstract class ScrollBehavior2 {
String toString() => '$runtimeType';
}
abstract class ScrollBehavior2Proxy extends ScrollBehavior2 {
ScrollBehavior2Proxy(this.parent) {
assert(parent != null);
}
final ScrollBehavior2 parent;
@override
Widget wrap(BuildContext context, Widget child, AxisDirection axisDirection) {
return parent.wrap(context, child, axisDirection);
}
@override
Widget createViewport({
Key key,
AxisDirection axisDirection: AxisDirection.down,
double anchor: 0.0,
ViewportOffset offset,
Key center,
List<Widget> children: const <Widget>[],
}) {
return parent.createViewport(
key: key,
axisDirection: axisDirection,
anchor: anchor,
offset: offset,
center: center,
children: children,
);
}
@override
ScrollPosition createScrollPosition(BuildContext context, Scrollable2State state, ScrollPosition oldPosition) {
return parent.createScrollPosition(context, state, oldPosition);
}
@override
bool shouldNotify(@checked ScrollBehavior2Proxy oldDelegate) {
return parent.shouldNotify(oldDelegate.parent);
}
}
class ScrollConfiguration2 extends InheritedWidget {
const ScrollConfiguration2({
Key key,
......@@ -440,9 +491,8 @@ class Scrollable2 extends StatefulWidget {
@override
Scrollable2State createState() => new Scrollable2State();
ScrollBehavior2 getScrollBehavior(BuildContext context) {
return scrollBehavior
?? ScrollConfiguration2.of(context)
static ScrollBehavior2 getScrollBehavior(BuildContext context) {
return ScrollConfiguration2.of(context)
?? new ViewportScrollBehavior();
}
......@@ -495,7 +545,7 @@ class Scrollable2State extends State<Scrollable2> with TickerProviderStateMixin
// only call this from places that will definitely trigger a rebuild
void _updatePosition() {
_scrollBehavior = config.getScrollBehavior(context);
_scrollBehavior = config.scrollBehavior ?? Scrollable2.getScrollBehavior(context);
final ScrollPosition oldPosition = position;
_position = _scrollBehavior.createScrollPosition(context, this, oldPosition);
assert(position != null);
......@@ -640,7 +690,7 @@ class Scrollable2State extends State<Scrollable2> with TickerProviderStateMixin
child: new IgnorePointer(
key: _ignorePointerKey,
ignoring: _shouldIgnorePointer,
child: new Viewport2(
child: _scrollBehavior.createViewport(
key: _viewportKey,
axisDirection: config.axisDirection,
anchor: config.anchor,
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/rendering.dart';
import 'basic.dart';
import 'framework.dart';
import 'scrollable.dart';
class SingleChildScrollView extends StatelessWidget {
SingleChildScrollView({
Key key,
this.scrollDirection: Axis.vertical,
this.initialScrollOffset: 0.0,
this.child,
}) : super(key: key) {
assert(scrollDirection != null);
assert(initialScrollOffset != null);
}
final Axis scrollDirection;
final double initialScrollOffset;
final Widget child;
AxisDirection _getDirection(BuildContext context) {
// TODO(abarth): Consider reading direction.
switch (scrollDirection) {
case Axis.horizontal:
return AxisDirection.right;
case Axis.vertical:
return AxisDirection.down;
}
return null;
}
@override
Widget build(BuildContext context) {
final ScrollBehavior2 scrollBehavior = new _SingleChildScrollViewBehavior(
Scrollable2.getScrollBehavior(context)
);
return new Scrollable2(
axisDirection: _getDirection(context),
initialScrollOffset: initialScrollOffset,
scrollBehavior: scrollBehavior,
children: child == null ? const <Widget>[] : <Widget>[ child ],
);
}
}
class _SingleChildScrollViewBehavior extends ScrollBehavior2Proxy {
_SingleChildScrollViewBehavior(ScrollBehavior2 parent) : super(parent);
@override
Widget createViewport({
Key key,
AxisDirection axisDirection: AxisDirection.down,
double anchor: 0.0,
ViewportOffset offset,
Key center,
List<Widget> children: const <Widget>[],
}) {
assert(anchor == 0.0);
assert(center == null);
assert(children.length <= 1);
return new _SingleChildViewport(
key: key,
axisDirection: axisDirection,
offset: offset,
child: children.isEmpty ? null : children[0],
);
}
}
class _SingleChildViewport extends SingleChildRenderObjectWidget {
_SingleChildViewport({
Key key,
this.axisDirection: AxisDirection.down,
this.offset,
Widget child,
}) : super(key: key, child: child) {
assert(axisDirection != null);
}
final AxisDirection axisDirection;
final ViewportOffset offset;
@override
_RenderSingleChildViewport createRenderObject(BuildContext context) {
return new _RenderSingleChildViewport(
axisDirection: axisDirection,
offset: offset,
);
}
@override
void updateRenderObject(BuildContext context, _RenderSingleChildViewport renderObject) {
// Order dependency: The offset setter reads the axis direction.
renderObject
..axisDirection = axisDirection
..offset = offset;
}
}
class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
_RenderSingleChildViewport({
AxisDirection axisDirection: AxisDirection.down,
ViewportOffset offset,
RenderBox child,
}) : _axisDirection = axisDirection,
_offset = offset {
assert(axisDirection != null);
assert(offset != null);
this.child = child;
}
AxisDirection get axisDirection => _axisDirection;
AxisDirection _axisDirection;
set axisDirection(AxisDirection value) {
assert(value != null);
if (value == _axisDirection)
return;
_axisDirection = value;
markNeedsLayout();
}
Axis get axis => axisDirectionToAxis(axisDirection);
ViewportOffset get offset => _offset;
ViewportOffset _offset;
set offset(ViewportOffset value) {
assert(value != null);
if (value == _offset)
return;
if (attached)
_offset.removeListener(markNeedsLayout);
if (_offset.pixels != value.pixels)
markNeedsLayout();
_offset = value;
if (attached)
_offset.addListener(markNeedsLayout);
// If we already have a size, then we should re-report the dimensions
// to the new ViewportOffset. If we don't then we'll report them when
// we establish the dimensions later, so don't worry about it now.
if (hasSize) {
assert(_minScrollExtent != null);
assert(_maxScrollExtent != null);
assert(_effectiveExtent != null);
offset.applyViewportDimension(_effectiveExtent);
if (offset.applyContentDimensions(_minScrollExtent, _maxScrollExtent))
markNeedsPaint();
}
}
@override
void setupParentData(RenderObject child) {
// We don't actually use the offset argument in BoxParentData, so let's
// avoid allocating it at all.
if (child.parentData is! ParentData)
child.parentData = new ParentData();
}
@override
void attach(PipelineOwner owner) {
super.attach(owner);
_offset.addListener(markNeedsPaint);
}
@override
void detach() {
_offset.removeListener(markNeedsPaint);
super.detach();
}
@override
bool get isRepaintBoundary => true;
double get _effectiveExtent {
assert(hasSize);
switch (axis) {
case Axis.vertical:
return size.height;
case Axis.horizontal:
return size.width;
}
return null;
}
double get _minScrollExtent {
assert(hasSize);
return 0.0;
}
double get _maxScrollExtent {
assert(hasSize);
if (child == null)
return 0.0;
return math.max(0.0, child.size.height - size.height);
}
BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
switch (axis) {
case Axis.horizontal:
return constraints.heightConstraints();
case Axis.vertical:
return constraints.widthConstraints();
}
return null;
}
@override
double computeMinIntrinsicWidth(double height) {
if (child != null)
return child.getMinIntrinsicWidth(height);
return 0.0;
}
@override
double computeMaxIntrinsicWidth(double height) {
if (child != null)
return child.getMaxIntrinsicWidth(height);
return 0.0;
}
@override
double computeMinIntrinsicHeight(double width) {
if (child != null)
return child.getMinIntrinsicHeight(width);
return 0.0;
}
@override
double computeMaxIntrinsicHeight(double width) {
if (child != null)
return child.getMaxIntrinsicHeight(width);
return 0.0;
}
// We don't override computeDistanceToActualBaseline(), because we
// want the default behavior (returning null). Otherwise, as you
// scroll, it would shift in its parent if the parent was baseline-aligned,
// which makes no sense.
@override
void performLayout() {
if (child == null) {
size = constraints.smallest;
} else {
child.layout(_getInnerConstraints(constraints), parentUsesSize: true);
size = constraints.constrain(child.size);
}
offset.applyViewportDimension(_effectiveExtent);
offset.applyContentDimensions(_minScrollExtent, _maxScrollExtent);
}
Offset get _scrollOffsetAsOffset {
assert(axisDirection != null);
switch (axisDirection) {
case AxisDirection.up:
return new Offset(0.0, _offset.pixels);
case AxisDirection.down:
return new Offset(0.0, -_offset.pixels);
case AxisDirection.left:
return new Offset(_offset.pixels, 0.0);
case AxisDirection.right:
return new Offset(-_offset.pixels, 0.0);
}
return null;
}
bool _shouldClipAtPaintOffset(Offset paintOffset) {
assert(child != null);
return paintOffset < Offset.zero || !(Offset.zero & size).contains((paintOffset & child.size).bottomRight);
}
@override
void paint(PaintingContext context, Offset offset) {
if (child != null) {
final Offset paintOffset = _scrollOffsetAsOffset;
void paintContents(PaintingContext context, Offset offset) {
context.paintChild(child, offset + paintOffset);
}
if (_shouldClipAtPaintOffset(paintOffset)) {
context.pushClipRect(needsCompositing, offset, Point.origin & size, paintContents);
} else {
paintContents(context, offset);
}
}
}
@override
void applyPaintTransform(RenderBox child, Matrix4 transform) {
final Offset paintOffset = _scrollOffsetAsOffset;
transform.translate(paintOffset.dx, paintOffset.dy);
}
@override
Rect describeApproximatePaintClip(RenderObject child) {
if (child != null && _shouldClipAtPaintOffset(_scrollOffsetAsOffset))
return Point.origin & size;
return null;
}
@override
bool hitTestChildren(HitTestResult result, { Point position }) {
if (child != null) {
final Point transformed = position + -_scrollOffsetAsOffset;
return child.hitTest(result, position: transformed);
}
return false;
}
}
......@@ -54,6 +54,7 @@ export 'src/widgets/scrollable.dart';
export 'src/widgets/scrollable_grid.dart';
export 'src/widgets/scrollable_list.dart';
export 'src/widgets/semantics_debugger.dart';
export 'src/widgets/single_child_scroll_view.dart';
export 'src/widgets/size_changed_layout_notifier.dart';
export 'src/widgets/status_transitions.dart';
export 'src/widgets/table.dart';
......
......@@ -11,7 +11,7 @@ void main() {
bool isExpanded;
await tester.pumpWidget(
new ScrollableViewport(
new SingleChildScrollView(
child: new ExpansionPanelList(
expansionCallback: (int _index, bool _isExpanded) {
index = _index;
......@@ -42,7 +42,7 @@ void main() {
// now expand the child panel
await tester.pumpWidget(
new ScrollableViewport(
new SingleChildScrollView(
child: new ExpansionPanelList(
expansionCallback: (int _index, bool _isExpanded) {
index = _index;
......
......@@ -65,7 +65,7 @@ void main() {
testWidgets('MergeableMaterial empty', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial()
)
)
......@@ -78,7 +78,7 @@ void main() {
testWidgets('MergeableMaterial update slice', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -99,7 +99,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -122,7 +122,7 @@ void main() {
testWidgets('MergeableMaterial swap slices', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -152,7 +152,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -187,7 +187,7 @@ void main() {
testWidgets('MergeableMaterial paints shadows', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -215,7 +215,7 @@ void main() {
testWidgets('MergeableMaterial merge gap', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -249,7 +249,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -288,7 +288,7 @@ void main() {
testWidgets('MergeableMaterial separate slices', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -318,7 +318,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -360,7 +360,7 @@ void main() {
testWidgets('MergeableMaterial separate merge seaparate', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -390,7 +390,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -430,7 +430,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -467,7 +467,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -509,7 +509,7 @@ void main() {
testWidgets('MergeableMaterial insert slice', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -539,7 +539,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -577,7 +577,7 @@ void main() {
testWidgets('MergeableMaterial remove slice', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -614,7 +614,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -646,7 +646,7 @@ void main() {
testWidgets('MergeableMaterial insert chunk', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -676,7 +676,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -730,7 +730,7 @@ void main() {
testWidgets('MergeableMaterial remove chunk', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -775,7 +775,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -814,7 +814,7 @@ void main() {
testWidgets('MergeableMaterial replace gap with chunk', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -848,7 +848,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -902,7 +902,7 @@ void main() {
testWidgets('MergeableMaterial replace chunk with gap', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -947,7 +947,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
children: <MergeableMaterialItem>[
new MaterialSlice(
......@@ -1001,7 +1001,7 @@ void main() {
testWidgets('MergeableMaterial dividers', (WidgetTester tester) async {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
hasDividers: true,
children: <MergeableMaterialItem>[
......@@ -1049,7 +1049,7 @@ void main() {
await tester.pumpWidget(
new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new MergeableMaterial(
hasDividers: true,
children: <MergeableMaterialItem>[
......
......@@ -244,7 +244,7 @@ void main() {
await tester.pumpWidget(
new MaterialApp(
home: new Scaffold(
body: new ScrollableViewport(
body: new SingleChildScrollView(
child: new Container(
decoration: new BoxDecoration(
backgroundColor: Colors.amber[500],
......
......@@ -31,7 +31,6 @@ void main() {
expect(a.hashCode, equals(b.hashCode));
expect(a.toString(), equals(b.toString()));
expect(a, hasOneLineDescription);
expect(a.scrollOffsetAsOffset, equals(const Offset(0.0, 0.0)));
expect(a.normalizedGrowthDirection, equals(GrowthDirection.forward));
SliverConstraints c = a.copyWith(
......@@ -53,21 +52,16 @@ void main() {
crossAxisExtent: 40.0,
);
expect(c, equals(d));
expect(c.scrollOffsetAsOffset, equals(const Offset(0.0, -10.0)));
expect(c.normalizedGrowthDirection, equals(GrowthDirection.forward));
expect(d.scrollOffsetAsOffset, equals(const Offset(0.0, -10.0)));
expect(d.normalizedGrowthDirection, equals(GrowthDirection.forward));
SliverConstraints e = d.copyWith(axisDirection: AxisDirection.right);
expect(e.scrollOffsetAsOffset, equals(const Offset(10.0, 0.0)));
expect(e.normalizedGrowthDirection, equals(GrowthDirection.reverse));
SliverConstraints f = d.copyWith(axisDirection: AxisDirection.left);
expect(f.scrollOffsetAsOffset, equals(const Offset(-10.0, 0.0)));
expect(f.normalizedGrowthDirection, equals(GrowthDirection.forward));
SliverConstraints g = d.copyWith(growthDirection: GrowthDirection.forward);
expect(g.scrollOffsetAsOffset, equals(const Offset(0.0, -10.0)));
expect(g.normalizedGrowthDirection, equals(GrowthDirection.reverse));
});
......
......@@ -26,7 +26,7 @@ void main() {
testWidgets('Shrink wraps in finite space', (WidgetTester tester) async {
GlobalKey alignKey = new GlobalKey();
await tester.pumpWidget(
new ScrollableViewport(
new SingleChildScrollView(
child: new Align(
key: alignKey,
child: new Container(
......
......@@ -71,12 +71,34 @@ class TestScrollPosition extends ScrollPosition {
class TestScrollBehavior extends ScrollBehavior2 {
TestScrollBehavior(this.extentMultiplier);
final double extentMultiplier;
@override
Widget wrap(BuildContext context, Widget child, AxisDirection axisDirection) => child;
@override
Widget createViewport({
Key key,
AxisDirection axisDirection: AxisDirection.down,
double anchor: 0.0,
ViewportOffset offset,
Key center,
List<Widget> children: const <Widget>[],
}) {
return new Viewport2(
key: key,
axisDirection: axisDirection,
anchor: anchor,
offset: offset,
center: center,
children: children,
);
}
@override
ScrollPosition createScrollPosition(BuildContext context, Scrollable2State state, ScrollPosition oldPosition) {
return new TestScrollPosition(extentMultiplier, state, ViewportScrollBehavior.defaultScrollTolerances, oldPosition);
}
@override
bool shouldNotify(TestScrollBehavior oldDelegate) {
return extentMultiplier != oldDelegate.extentMultiplier;
......@@ -103,4 +125,4 @@ void main() {
));
expect(key.currentState.position.getMetrics().extentInside, 2.0);
});
}
\ No newline at end of file
}
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
void main() {
testWidgets('SingleChildScrollView control test', (WidgetTester tester) async {
await tester.pumpWidget(new SingleChildScrollView(
child: new Container(
height: 2000.0,
decoration: const BoxDecoration(
backgroundColor: const Color(0xFF00FF00),
),
),
));
RenderBox box = tester.renderObject(find.byType(Container));
expect(box.localToGlobal(Point.origin), equals(Point.origin));
await tester.scroll(find.byType(SingleChildScrollView), const Offset(-200.0, -200.0));
expect(box.localToGlobal(Point.origin), equals(const Point(0.0, -200.0)));
});
}
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