Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
3b9b5ace
Unverified
Commit
3b9b5ace
authored
Jun 07, 2018
by
Michael Goderbauer
Committed by
GitHub
Jun 07, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
showOnScreen Improvements (#18252)
parent
c53245c6
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
1463 additions
and
86 deletions
+1463
-86
list_wheel_viewport.dart
packages/flutter/lib/src/rendering/list_wheel_viewport.dart
+41
-17
object.dart
packages/flutter/lib/src/rendering/object.dart
+31
-6
viewport.dart
packages/flutter/lib/src/rendering/viewport.dart
+187
-48
viewport_offset.dart
packages/flutter/lib/src/rendering/viewport_offset.dart
+22
-1
scroll_position.dart
packages/flutter/lib/src/widgets/scroll_position.dart
+2
-1
single_child_scroll_view.dart
...ges/flutter/lib/src/widgets/single_child_scroll_view.dart
+33
-13
viewport_test.dart
packages/flutter/test/rendering/viewport_test.dart
+540
-0
list_wheel_scroll_view_test.dart
...ges/flutter/test/widgets/list_wheel_scroll_view_test.dart
+123
-0
single_child_scroll_view_test.dart
...s/flutter/test/widgets/single_child_scroll_view_test.dart
+484
-0
No files found.
packages/flutter/lib/src/rendering/list_wheel_viewport.dart
View file @
3b9b5ace
...
...
@@ -4,6 +4,7 @@
import
'dart:math'
as
math
;
import
'package:flutter/animation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:vector_math/vector_math_64.dart'
show
Matrix4
;
...
...
@@ -614,27 +615,50 @@ class RenderListWheelViewport
}
@override
double
getOffsetToReveal
(
RenderObject
target
,
double
alignment
)
{
final
ListWheelParentData
parentData
=
target
.
parentData
;
final
double
centerPosition
=
parentData
.
offset
.
dy
;
if
(
alignment
<
0.5
)
{
return
centerPosition
+
_topScrollMarginExtent
*
alignment
*
2.0
;
}
else
if
(
alignment
>
0.5
)
{
return
centerPosition
-
_topScrollMarginExtent
*
(
alignment
-
0.5
)
*
2.0
;
}
else
{
return
centerPosition
;
}
RevealedOffset
getOffsetToReveal
(
RenderObject
target
,
double
alignment
,
{
Rect
rect
})
{
// `target` is only fully revealed when in the selected/center position. Therefore,
// this method always returns the offset that shows `target` in the center position,
// which is the same offset for all `alignment` values.
rect
??=
target
.
paintBounds
;
// `child` will be the last RenderObject before the viewport when walking up from `target`.
RenderObject
child
=
target
;
while
(
child
.
parent
!=
this
)
child
=
child
.
parent
;
final
ListWheelParentData
parentData
=
child
.
parentData
;
final
double
targetOffset
=
parentData
.
offset
.
dy
;
// the so-called "centerPosition"
final
Matrix4
transform
=
target
.
getTransformTo
(
this
);
final
Rect
bounds
=
MatrixUtils
.
transformRect
(
transform
,
rect
);
final
Rect
targetRect
=
bounds
.
translate
(
0.0
,
(
size
.
height
-
itemExtent
)
/
2
);
return
new
RevealedOffset
(
offset:
targetOffset
,
rect:
targetRect
);
}
@override
void
showOnScreen
([
RenderObject
child
])
{
if
(
child
!=
null
)
{
// Shows the child in the selected/center position.
offset
.
jumpTo
(
getOffsetToReveal
(
child
,
0.5
));
void
showOnScreen
({
RenderObject
descendant
,
Rect
rect
,
Duration
duration
=
Duration
.
zero
,
Curve
curve
=
Curves
.
ease
,
})
{
if
(
descendant
!=
null
)
{
// Shows the descendant in the selected/center position.
final
RevealedOffset
revealedOffset
=
getOffsetToReveal
(
descendant
,
0.5
,
rect:
rect
);
if
(
duration
==
Duration
.
zero
)
{
offset
.
jumpTo
(
revealedOffset
.
offset
);
}
else
{
offset
.
animateTo
(
revealedOffset
.
offset
,
duration:
duration
,
curve:
curve
);
}
rect
=
revealedOffset
.
rect
;
}
// Make sure the viewport itself is on screen.
super
.
showOnScreen
();
super
.
showOnScreen
(
rect:
rect
,
duration:
duration
,
curve:
curve
,
);
}
}
packages/flutter/lib/src/rendering/object.dart
View file @
3b9b5ace
...
...
@@ -5,6 +5,7 @@
import
'dart:developer'
;
import
'dart:ui'
as
ui
show
PictureRecorder
;
import
'package:flutter/animation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/painting.dart'
;
...
...
@@ -2036,6 +2037,9 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
/// An estimate of the bounds within which this render object will paint.
/// Useful for debugging flags such as [debugPaintLayerBordersEnabled].
///
/// These are also the bounds used by [showOnScreen] to make a [RenderObject]
/// visible on screen.
Rect
get
paintBounds
;
/// Override this method to paint debugging information.
...
...
@@ -2570,14 +2574,35 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
@override
List
<
DiagnosticsNode
>
debugDescribeChildren
()
=>
<
DiagnosticsNode
>[];
/// Attempt to make this or a descendant RenderObject visible on screen.
///
/// If [child] is provided, that [RenderObject] is made visible. If [child] is
/// omitted, this [RenderObject] is made visible.
void
showOnScreen
([
RenderObject
child
])
{
/// Attempt to make (a portion of) this or a descendant [RenderObject] visible
/// on screen.
///
/// If `descendant` is provided, that [RenderObject] is made visible. If
/// `descendant` is omitted, this [RenderObject] is made visible.
///
/// The optional `rect` parameter describes which area of that [RenderObject]
/// should be shown on screen. If `rect` is null, the entire
/// [RenderObject] (as defined by its [paintBounds]) will be revealed. The
/// `rect` parameter is interpreted relative to the coordinate system of
/// `descendant` if that argument is provided and relative to this
/// [RenderObject] otherwise.
///
/// The `duration` parameter can be set to a non-zero value to bring the
/// target object on screen in an animation defined by `curve`.
void
showOnScreen
({
RenderObject
descendant
,
Rect
rect
,
Duration
duration
=
Duration
.
zero
,
Curve
curve
=
Curves
.
ease
,
})
{
if
(
parent
is
RenderObject
)
{
final
RenderObject
renderParent
=
parent
;
renderParent
.
showOnScreen
(
child
??
this
);
renderParent
.
showOnScreen
(
descendant:
descendant
??
this
,
rect:
rect
,
duration:
duration
,
curve:
curve
,
);
}
}
}
...
...
packages/flutter/lib/src/rendering/viewport.dart
View file @
3b9b5ace
...
...
@@ -4,6 +4,7 @@
import
'dart:math'
as
math
;
import
'package:flutter/animation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/semantics.dart'
;
...
...
@@ -40,7 +41,14 @@ abstract class RenderAbstractViewport extends RenderObject {
return
null
;
}
/// Returns the offset that would be needed to reveal the target render object.
/// Returns the offset that would be needed to reveal the `target`
/// [RenderObject].
///
/// The optional `rect` parameter describes which area of that `target` object
/// should be revealed in the viewport. If `rect` is null, the entire
/// `target` [RenderObject] (as defined by its [RenderObject.paintBounds])
/// will be revealed. If `rect` is provided it has to be given in the
/// coordinate system of the `target` object.
///
/// The `alignment` argument describes where the target should be positioned
/// after applying the returned offset. If `alignment` is 0.0, the child must
...
...
@@ -52,7 +60,15 @@ abstract class RenderAbstractViewport extends RenderObject {
/// The target might not be a direct child of this viewport but it must be a
/// descendant of the viewport and there must not be any other
/// [RenderAbstractViewport] objects between the target and this object.
double
getOffsetToReveal
(
RenderObject
target
,
double
alignment
);
///
/// This method assumes that the content of the viewport moves linearly, i.e.
/// when the offset of the viewport is changed by x then `target` also moves
/// by x within the viewport.
///
/// See also:
///
/// * [RevealedOffset], which describes the return value of this method.
RevealedOffset
getOffsetToReveal
(
RenderObject
target
,
double
alignment
,
{
Rect
rect
});
/// The default value for the cache extent of the viewport.
///
...
...
@@ -63,6 +79,59 @@ abstract class RenderAbstractViewport extends RenderObject {
static
const
double
defaultCacheExtent
=
250.0
;
}
/// Return value for [RenderAbstractViewport.getOffsetToReveal].
///
/// It indicates the [offset] required to reveal an element in a viewport and
/// the [rect] position said element would have in the viewport at that
/// [offset].
class
RevealedOffset
{
/// Instantiates a return value for [RenderAbstractViewport.getOffsetToReveal].
const
RevealedOffset
({
@required
this
.
offset
,
@required
this
.
rect
,
})
:
assert
(
offset
!=
null
),
assert
(
rect
!=
null
);
/// Offset for the viewport to reveal a specific element in the viewport.
///
/// See also:
///
/// * [RenderAbstractViewport.getOffsetToReveal], which calculates this
/// value for a specific element.
final
double
offset
;
/// The [Rect] in the outer coordinate system of the viewport at which the
/// to-be-revealed element would be located if the viewport's offset is set
/// to [offset].
///
/// A viewport usually has two coordinate systems and works as an adapter
/// between the two:
///
/// The inner coordinate system has its origin at the top left corner of the
/// content that moves inside the viewport. The origin of this coordinate
/// system usually moves around relative to the leading edge of the viewport
/// when the viewport offset changes.
///
/// The outer coordinate system has its origin at the top left corner of the
/// visible part of the viewport. This origin stays at the same position
/// regardless of the current viewport offset.
///
/// In other words: [rect] describes where the revealed element would be
/// located relative to the top left corner of the visible part of the
/// viewport if the viewport's offset is set to [offset].
///
/// See also:
///
/// * [RenderAbstractViewport.getOffsetToReveal], which calculates this
/// value for a specific element.
final
Rect
rect
;
@override
String
toString
()
{
return
'
$runtimeType
(offset:
$offset
, rect:
$rect
)'
;
}
}
/// A base class for render objects that are bigger on the inside.
///
/// This render object provides the shared code for render objects that host
...
...
@@ -512,14 +581,16 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
}
@override
double
getOffsetToReveal
(
RenderObject
target
,
double
alignment
)
{
RevealedOffset
getOffsetToReveal
(
RenderObject
target
,
double
alignment
,
{
Rect
rect
}
)
{
double
leadingScrollOffset
;
double
targetMainAxisExtent
;
RenderObject
descendant
;
rect
??=
target
.
paintBounds
;
if
(
target
is
RenderBox
)
{
final
RenderBox
targetBox
=
target
;
// The pivot will be the topmost child before we hit a RenderSliver.
RenderBox
pivot
=
targetBox
;
while
(
pivot
.
parent
is
RenderBox
)
pivot
=
pivot
.
parent
;
...
...
@@ -527,14 +598,11 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
assert
(
pivot
.
parent
!=
null
);
assert
(
pivot
.
parent
!=
this
);
assert
(
pivot
!=
this
);
assert
(
pivot
.
parent
is
RenderSliver
);
// TODO(abarth): Support other kinds of render objects besides slivers.
final
RenderSliver
pivotParent
=
pivot
.
parent
;
final
Matrix4
transform
=
targetBox
.
getTransformTo
(
pivot
);
final
Rect
bounds
=
MatrixUtils
.
transformRect
(
transform
,
targetBox
.
paintBounds
);
target
=
pivot
;
// TODO(abarth): Support other kinds of render objects besides slivers.
assert
(
target
.
parent
is
RenderSliver
);
final
RenderSliver
pivotParent
=
target
.
parent
;
final
Rect
bounds
=
MatrixUtils
.
transformRect
(
transform
,
rect
);
final
GrowthDirection
growthDirection
=
pivotParent
.
constraints
.
growthDirection
;
switch
(
applyGrowthDirectionToAxisDirection
(
axisDirection
,
growthDirection
))
{
...
...
@@ -580,7 +648,7 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
targetMainAxisExtent
=
targetSliver
.
geometry
.
scrollExtent
;
descendant
=
targetSliver
;
}
else
{
return
offset
.
pixels
;
return
new
RevealedOffset
(
offset:
offset
.
pixels
,
rect:
rect
)
;
}
// The child will be the topmost object before we get to the viewport.
...
...
@@ -615,7 +683,29 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
break
;
}
return
leadingScrollOffset
-
(
mainAxisExtent
-
targetMainAxisExtent
)
*
alignment
;
final
double
targetOffset
=
leadingScrollOffset
-
(
mainAxisExtent
-
targetMainAxisExtent
)
*
alignment
;
final
double
offsetDifference
=
offset
.
pixels
-
targetOffset
;
final
Matrix4
transform
=
target
.
getTransformTo
(
this
);
applyPaintTransform
(
child
,
transform
);
Rect
targetRect
=
MatrixUtils
.
transformRect
(
transform
,
rect
);
switch
(
axisDirection
)
{
case
AxisDirection
.
down
:
targetRect
=
targetRect
.
translate
(
0.0
,
offsetDifference
);
break
;
case
AxisDirection
.
right
:
targetRect
=
targetRect
.
translate
(
offsetDifference
,
0.0
);
break
;
case
AxisDirection
.
up
:
targetRect
=
targetRect
.
translate
(
0.0
,
-
offsetDifference
);
break
;
case
AxisDirection
.
left
:
targetRect
=
targetRect
.
translate
(-
offsetDifference
,
0.0
);
break
;
}
return
new
RevealedOffset
(
offset:
targetOffset
,
rect:
targetRect
);
}
/// The offset at which the given `child` should be painted.
...
...
@@ -646,8 +736,6 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
return
null
;
}
// TODO(ianh): semantics - shouldn't walk the invisible children
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
...
...
@@ -783,42 +871,76 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
Iterable
<
RenderSliver
>
get
childrenInHitTestOrder
;
@override
void
showOnScreen
([
RenderObject
child
])
{
RenderViewportBase
.
showInViewport
(
child:
child
,
viewport:
this
,
offset:
offset
);
// Make sure the viewport itself is on screen.
super
.
showOnScreen
();
void
showOnScreen
({
RenderObject
descendant
,
Rect
rect
,
Duration
duration
=
Duration
.
zero
,
Curve
curve
=
Curves
.
ease
,
})
{
final
Rect
newRect
=
RenderViewportBase
.
showInViewport
(
descendant:
descendant
,
viewport:
this
,
offset:
offset
,
rect:
rect
,
duration:
duration
,
curve:
curve
,
);
super
.
showOnScreen
(
rect:
newRect
,
duration:
duration
,
curve:
curve
,
);
}
/// Make the given `child` of the given `viewport` fully visible in the
/// `viewport` by manipulating the provided [ViewportOffset] `offset`.
/// Make (a portion of) the given `descendant` of the given `viewport` fully
/// visible in the `viewport` by manipulating the provided [ViewportOffset]
/// `offset`.
///
/// The optional `rect` parameter describes which area of the `descendant`
/// should be shown in the viewport. If `rect` is null, the entire
/// `descendant` will be revealed. The `rect` parameter is interpreted
/// relative to the coordinate system of `descendant`.
///
/// The returned [Rect] describes the new location of `descendant` or `rect`
/// in the viewport after it has been revealed. See [RevealedOffset.rect]
/// for a full definition of this [Rect].
///
/// The parameters `viewport` and `offset` are required and cannot be null.
/// If `child` is null this is a no-op.
static
void
showInViewport
({
RenderObject
child
,
/// If `descendant` is null, this is a no-op and `rect` is returned.
///
/// If both `decedent` and `rect` are null, null is returned because there is
/// nothing to be shown in the viewport.
///
/// The `duration` parameter can be set to a non-zero value to animate the
/// target object into the viewport with an animation defined by `curve`.
static
Rect
showInViewport
({
RenderObject
descendant
,
Rect
rect
,
@required
RenderAbstractViewport
viewport
,
@required
ViewportOffset
offset
,
Duration
duration
=
Duration
.
zero
,
Curve
curve
=
Curves
.
ease
,
})
{
assert
(
viewport
!=
null
);
assert
(
offset
!=
null
);
if
(
child
==
null
)
{
return
;
if
(
descendant
==
null
)
{
return
rect
;
}
final
double
leadingEdgeOffset
=
viewport
.
getOffsetToReveal
(
child
,
0.0
);
final
double
trailingEdgeOffset
=
viewport
.
getOffsetToReveal
(
child
,
1.0
);
final
RevealedOffset
leadingEdgeOffset
=
viewport
.
getOffsetToReveal
(
descendant
,
0.0
,
rect:
rect
);
final
RevealedOffset
trailingEdgeOffset
=
viewport
.
getOffsetToReveal
(
descendant
,
1.0
,
rect:
rect
);
final
double
currentOffset
=
offset
.
pixels
;
// scrollOffset
// 0 +---------+
// | |
// _ | |
// viewport position | | |
//
with `child
` at | | | _
// trailing edge |_ | xxxxxxx | | viewport position
//
| | | with `child
` at
// | | _| leading edge
// | |
// 800 +---------+
//
scrollOffset
//
0 +---------+
//
| |
//
_ | |
//
viewport position | | |
//
with `descendant
` at | | | _
//
trailing edge |_ | xxxxxxx | | viewport position
//
| | | with `descendant
` at
//
| | _| leading edge
//
| |
//
800 +---------+
//
// `trailingEdgeOffset`: Distance from scrollOffset 0 to the start of the
// viewport on the left in image above.
...
...
@@ -829,19 +951,36 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
// to `trailingEdgeOffset`, the one on the right by setting it to
// `leadingEdgeOffset`.
assert
(
leadingEdgeOffset
>=
trailingEdgeOffset
);
RevealedOffset
targetOffset
;
if
(
leadingEdgeOffset
.
offset
<
trailingEdgeOffset
.
offset
)
{
// `descendant` is too big to be visible on screen in its entirety. Let's
// align it with the edge that requires the least amount of scrolling.
final
double
leadingEdgeDiff
=
(
offset
.
pixels
-
leadingEdgeOffset
.
offset
).
abs
();
final
double
trailingEdgeDiff
=
(
offset
.
pixels
-
trailingEdgeOffset
.
offset
).
abs
();
targetOffset
=
leadingEdgeDiff
<
trailingEdgeDiff
?
leadingEdgeOffset
:
trailingEdgeOffset
;
}
else
if
(
currentOffset
>
leadingEdgeOffset
.
offset
)
{
// `descendant` currently starts above the leading edge and can be shown
// fully on screen by scrolling down (which means: moving viewport up).
targetOffset
=
leadingEdgeOffset
;
}
else
if
(
currentOffset
<
trailingEdgeOffset
.
offset
)
{
// `descendant currently ends below the trailing edge and can be shown
// fully on screen by scrolling up (which means: moving viewport down)
targetOffset
=
trailingEdgeOffset
;
}
else
{
// `descendant` is between leading and trailing edge and hence already
// fully shown on screen. No action necessary.
final
Matrix4
transform
=
descendant
.
getTransformTo
(
viewport
.
parent
);
return
MatrixUtils
.
transformRect
(
transform
,
rect
??
descendant
.
paintBounds
);
}
assert
(
targetOffset
!=
null
);
if
(
currentOffset
>
leadingEdgeOffset
)
{
// `child` currently starts above the leading edge and can be shown fully
// on screen by scrolling down (which means: moving viewport up).
offset
.
jumpTo
(
leadingEdgeOffset
);
}
else
if
(
currentOffset
<
trailingEdgeOffset
)
{
// `child currently ends below the trailing edge and can be shown fully
// on screen by scrolling up (which means: moving viewport down)
offset
.
jumpTo
(
trailingEdgeOffset
);
if
(
duration
==
Duration
.
zero
)
{
offset
.
jumpTo
(
targetOffset
.
offset
);
}
else
{
offset
.
animateTo
(
targetOffset
.
offset
,
duration:
duration
,
curve:
curve
);
}
// else: `child` is between leading and trailing edge and hence already
// fully shown on screen. No action necessary.
return
targetOffset
.
rect
;
}
}
...
...
packages/flutter/lib/src/rendering/viewport_offset.dart
View file @
3b9b5ace
...
...
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'package:flutter/animation.dart'
;
import
'package:flutter/foundation.dart'
;
/// The direction of a scroll, relative to the positive scroll offset axis given
...
...
@@ -157,7 +160,7 @@ abstract class ViewportOffset extends ChangeNotifier {
/// [jumpTo] applies the change immediately and notifies its listeners.
void
correctBy
(
double
correction
);
/// Jumps
the scroll position
from its current value to the given value,
/// Jumps
[pixels]
from its current value to the given value,
/// without animation, and without checking if the new value is in range.
///
/// See also:
...
...
@@ -166,6 +169,18 @@ abstract class ViewportOffset extends ChangeNotifier {
/// and that defers the notification of its listeners until after layout.
void
jumpTo
(
double
pixels
);
/// Animates [pixels] from its current value to the given value.
///
/// The returned [Future] will complete when the animation ends, whether it
/// completed successfully or whether it was interrupted prematurely.
///
/// The duration must not be zero. To jump to a particular value without an
/// animation, use [jumpTo].
Future
<
Null
>
animateTo
(
double
to
,
{
@required
Duration
duration
,
@required
Curve
curve
,
});
/// The direction in which the user is trying to change [pixels], relative to
/// the viewport's [RenderViewport.axisDirection].
///
...
...
@@ -227,6 +242,12 @@ class _FixedViewportOffset extends ViewportOffset {
// Do nothing, viewport is fixed.
}
@override
Future
<
Null
>
animateTo
(
double
to
,
{
@required
Duration
duration
,
@required
Curve
curve
,
})
async
=>
null
;
@override
ScrollDirection
get
userScrollDirection
=>
ScrollDirection
.
idle
;
}
packages/flutter/lib/src/widgets/scroll_position.dart
View file @
3b9b5ace
...
...
@@ -497,7 +497,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
final
RenderAbstractViewport
viewport
=
RenderAbstractViewport
.
of
(
object
);
assert
(
viewport
!=
null
);
final
double
target
=
viewport
.
getOffsetToReveal
(
object
,
alignment
).
clamp
(
minScrollExtent
,
maxScrollExtent
);
final
double
target
=
viewport
.
getOffsetToReveal
(
object
,
alignment
).
offset
.
clamp
(
minScrollExtent
,
maxScrollExtent
);
if
(
target
==
pixels
)
return
new
Future
<
Null
>.
value
();
...
...
@@ -544,6 +544,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
/// animation, use [jumpTo].
///
/// The animation is typically handled by an [DrivenScrollActivity].
@override
Future
<
Null
>
animateTo
(
double
to
,
{
@required
Duration
duration
,
@required
Curve
curve
,
...
...
packages/flutter/lib/src/widgets/single_child_scroll_view.dart
View file @
3b9b5ace
...
...
@@ -484,17 +484,19 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix
offset
.
applyContentDimensions
(
_minScrollExtent
,
_maxScrollExtent
);
}
Offset
get
_paintOffset
{
Offset
get
_paintOffset
=>
_paintOffsetForPosition
(
offset
.
pixels
);
Offset
_paintOffsetForPosition
(
double
position
)
{
assert
(
axisDirection
!=
null
);
switch
(
axisDirection
)
{
case
AxisDirection
.
up
:
return
new
Offset
(
0.0
,
_offset
.
pixels
-
child
.
size
.
height
+
size
.
height
);
return
new
Offset
(
0.0
,
position
-
child
.
size
.
height
+
size
.
height
);
case
AxisDirection
.
down
:
return
new
Offset
(
0.0
,
-
_offset
.
pixels
);
return
new
Offset
(
0.0
,
-
position
);
case
AxisDirection
.
left
:
return
new
Offset
(
_offset
.
pixels
-
child
.
size
.
width
+
size
.
width
,
0.0
);
return
new
Offset
(
position
-
child
.
size
.
width
+
size
.
width
,
0.0
);
case
AxisDirection
.
right
:
return
new
Offset
(-
_offset
.
pixels
,
0.0
);
return
new
Offset
(-
position
,
0.0
);
}
return
null
;
}
...
...
@@ -544,13 +546,14 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix
}
@override
double
getOffsetToReveal
(
RenderObject
target
,
double
alignment
)
{
RevealedOffset
getOffsetToReveal
(
RenderObject
target
,
double
alignment
,
{
Rect
rect
})
{
rect
??=
target
.
paintBounds
;
if
(
target
is
!
RenderBox
)
return
offset
.
pixels
;
return
new
RevealedOffset
(
offset:
offset
.
pixels
,
rect:
rect
)
;
final
RenderBox
targetBox
=
target
;
final
Matrix4
transform
=
targetBox
.
getTransformTo
(
this
);
final
Rect
bounds
=
MatrixUtils
.
transformRect
(
transform
,
targetBox
.
paintBounds
);
final
Rect
bounds
=
MatrixUtils
.
transformRect
(
transform
,
rect
);
final
Size
contentSize
=
child
.
size
;
double
leadingScrollOffset
;
...
...
@@ -581,14 +584,31 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix
break
;
}
return
leadingScrollOffset
-
(
mainAxisExtent
-
targetMainAxisExtent
)
*
alignment
;
final
double
targetOffset
=
leadingScrollOffset
-
(
mainAxisExtent
-
targetMainAxisExtent
)
*
alignment
;
final
Rect
targetRect
=
bounds
.
shift
(
_paintOffsetForPosition
(
targetOffset
));
return
new
RevealedOffset
(
offset:
targetOffset
,
rect:
targetRect
);
}
@override
void
showOnScreen
([
RenderObject
child
])
{
RenderViewportBase
.
showInViewport
(
child:
child
,
viewport:
this
,
offset:
offset
);
// Make sure the viewport itself is on screen.
super
.
showOnScreen
();
void
showOnScreen
({
RenderObject
descendant
,
Rect
rect
,
Duration
duration
=
Duration
.
zero
,
Curve
curve
=
Curves
.
ease
,
})
{
final
Rect
newRect
=
RenderViewportBase
.
showInViewport
(
descendant:
descendant
,
viewport:
this
,
offset:
offset
,
rect:
rect
,
duration:
duration
,
curve:
curve
,
);
super
.
showOnScreen
(
rect:
newRect
,
duration:
duration
,
curve:
curve
,
);
}
@override
...
...
packages/flutter/test/rendering/viewport_test.dart
0 → 100644
View file @
3b9b5ace
// Copyright 2018 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:ui'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
void
main
(
)
{
testWidgets
(
'Viewport getOffsetToReveal - down'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
children
;
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
200.0
,
width:
300.0
,
child:
new
ListView
(
controller:
new
ScrollController
(
initialScrollOffset:
300.0
),
children:
children
=
new
List
<
Widget
>.
generate
(
20
,
(
int
i
)
{
return
new
Container
(
height:
100.0
,
width:
300.0
,
child:
new
Text
(
'Tile
$i
'
),
);
}),
),
),
),
),
);
final
RenderAbstractViewport
viewport
=
tester
.
allRenderObjects
.
firstWhere
((
RenderObject
r
)
=>
r
is
RenderAbstractViewport
);
final
RenderObject
target
=
tester
.
renderObject
(
find
.
byWidget
(
children
[
5
],
skipOffstage:
false
));
RevealedOffset
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
);
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
0.0
,
300.0
,
100.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
);
expect
(
revealed
.
offset
,
400.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
100.0
,
300.0
,
100.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
540.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
40.0
,
0.0
,
10.0
,
10.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
350.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
40.0
,
190.0
,
10.0
,
10.0
));
});
testWidgets
(
'Viewport getOffsetToReveal - right'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
children
;
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
300.0
,
width:
200.0
,
child:
new
ListView
(
scrollDirection:
Axis
.
horizontal
,
controller:
new
ScrollController
(
initialScrollOffset:
300.0
),
children:
children
=
new
List
<
Widget
>.
generate
(
20
,
(
int
i
)
{
return
new
Container
(
height:
300.0
,
width:
100.0
,
child:
new
Text
(
'Tile
$i
'
),
);
}),
),
),
),
),
);
final
RenderAbstractViewport
viewport
=
tester
.
allRenderObjects
.
firstWhere
((
RenderObject
r
)
=>
r
is
RenderAbstractViewport
);
final
RenderObject
target
=
tester
.
renderObject
(
find
.
byWidget
(
children
[
5
],
skipOffstage:
false
));
RevealedOffset
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
);
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
0.0
,
100.0
,
300.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
);
expect
(
revealed
.
offset
,
400.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
100.0
,
0.0
,
100.0
,
300.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
540.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
40.0
,
10.0
,
10.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
350.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
190.0
,
40.0
,
10.0
,
10.0
));
});
testWidgets
(
'Viewport getOffsetToReveal - up'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
children
;
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
200.0
,
width:
300.0
,
child:
new
ListView
(
controller:
new
ScrollController
(
initialScrollOffset:
300.0
),
reverse:
true
,
children:
children
=
new
List
<
Widget
>.
generate
(
20
,
(
int
i
)
{
return
new
Container
(
height:
100.0
,
width:
300.0
,
child:
new
Text
(
'Tile
$i
'
),
);
}),
),
),
),
),
);
final
RenderAbstractViewport
viewport
=
tester
.
allRenderObjects
.
firstWhere
((
RenderObject
r
)
=>
r
is
RenderAbstractViewport
);
final
RenderObject
target
=
tester
.
renderObject
(
find
.
byWidget
(
children
[
5
],
skipOffstage:
false
));
RevealedOffset
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
);
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
100.0
,
300.0
,
100.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
);
expect
(
revealed
.
offset
,
400.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
0.0
,
300.0
,
100.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
550.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
40.0
,
190.0
,
10.0
,
10.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
360.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
40.0
,
0.0
,
10.0
,
10.0
));
});
testWidgets
(
'Viewport getOffsetToReveal - left'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
children
;
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
300.0
,
width:
200.0
,
child:
new
ListView
(
scrollDirection:
Axis
.
horizontal
,
reverse:
true
,
controller:
new
ScrollController
(
initialScrollOffset:
300.0
),
children:
children
=
new
List
<
Widget
>.
generate
(
20
,
(
int
i
)
{
return
new
Container
(
height:
300.0
,
width:
100.0
,
child:
new
Text
(
'Tile
$i
'
),
);
}),
),
),
),
),
);
final
RenderAbstractViewport
viewport
=
tester
.
allRenderObjects
.
firstWhere
((
RenderObject
r
)
=>
r
is
RenderAbstractViewport
);
final
RenderObject
target
=
tester
.
renderObject
(
find
.
byWidget
(
children
[
5
],
skipOffstage:
false
));
RevealedOffset
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
);
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
100.0
,
0.0
,
100.0
,
300.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
);
expect
(
revealed
.
offset
,
400.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
0.0
,
100.0
,
300.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
550.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
190.0
,
40.0
,
10.0
,
10.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
360.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
40.0
,
10.0
,
10.0
));
});
testWidgets
(
'Nested Viewports showOnScreen'
,
(
WidgetTester
tester
)
async
{
final
List
<
List
<
Widget
>>
children
=
new
List
<
List
<
Widget
>>(
10
);
final
List
<
ScrollController
>
controllersX
=
new
List
<
ScrollController
>.
generate
(
10
,
(
int
i
)
=>
new
ScrollController
(
initialScrollOffset:
400.0
));
final
ScrollController
controllerY
=
new
ScrollController
(
initialScrollOffset:
400.0
);
/// Builds a gird:
///
/// <- x ->
/// 0 1 2 3 4 5 6 7 8 9
/// 0 c c c c c c c c c c
/// 1 c c c c c c c c c c
/// 2 c c c c c c c c c c
/// 3 c c c c c c c c c c y
/// 4 c c c c v v c c c c
/// 5 c c c c v v c c c c
/// 6 c c c c c c c c c c
/// 7 c c c c c c c c c c
/// 8 c c c c c c c c c c
/// 9 c c c c c c c c c c
///
/// Each c is a 100x100 container, v are containers visible in initial
/// viewport.
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
200.0
,
width:
200.0
,
child:
new
ListView
(
controller:
controllerY
,
children:
new
List
<
Widget
>.
generate
(
10
,
(
int
y
)
{
return
Container
(
height:
100.0
,
child:
new
ListView
(
scrollDirection:
Axis
.
horizontal
,
controller:
controllersX
[
y
],
children:
children
[
y
]
=
new
List
<
Widget
>.
generate
(
10
,
(
int
x
)
{
return
new
Container
(
height:
100.0
,
width:
100.0
,
child:
new
Text
(
'
$x
,
$y
'
),
);
}),
),
);
}),
),
),
),
),
);
// Already in viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
4
][
4
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllersX
[
4
].
offset
,
400.0
);
expect
(
controllerY
.
offset
,
400.0
);
controllersX
[
4
].
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Above viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
3
][
4
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllersX
[
3
].
offset
,
400.0
);
expect
(
controllerY
.
offset
,
300.0
);
controllersX
[
3
].
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Below viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
6
][
4
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllersX
[
6
].
offset
,
400.0
);
expect
(
controllerY
.
offset
,
500.0
);
controllersX
[
6
].
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Left of viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
4
][
3
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllersX
[
4
].
offset
,
300.0
);
expect
(
controllerY
.
offset
,
400.0
);
controllersX
[
4
].
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Right of viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
4
][
6
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllersX
[
4
].
offset
,
500.0
);
expect
(
controllerY
.
offset
,
400.0
);
controllersX
[
4
].
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Above and left of viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
3
][
3
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllersX
[
3
].
offset
,
300.0
);
expect
(
controllerY
.
offset
,
300.0
);
controllersX
[
3
].
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Below and left of viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
6
][
3
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllersX
[
6
].
offset
,
300.0
);
expect
(
controllerY
.
offset
,
500.0
);
controllersX
[
6
].
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Above and right of viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
3
][
6
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllersX
[
3
].
offset
,
500.0
);
expect
(
controllerY
.
offset
,
300.0
);
controllersX
[
3
].
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Below and right of viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
6
][
6
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllersX
[
6
].
offset
,
500.0
);
expect
(
controllerY
.
offset
,
500.0
);
controllersX
[
6
].
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Below and right of viewport with animations
tester
.
renderObject
(
find
.
byWidget
(
children
[
6
][
6
],
skipOffstage:
false
)).
showOnScreen
(
duration:
const
Duration
(
seconds:
2
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
tester
.
hasRunningAnimations
,
isTrue
);
expect
(
controllersX
[
6
].
offset
,
greaterThan
(
400.0
));
expect
(
controllersX
[
6
].
offset
,
lessThan
(
500.0
));
expect
(
controllerY
.
offset
,
greaterThan
(
400.0
));
expect
(
controllerY
.
offset
,
lessThan
(
500.0
));
await
tester
.
pumpAndSettle
();
expect
(
controllersX
[
6
].
offset
,
500.0
);
expect
(
controllerY
.
offset
,
500.0
);
});
group
(
'Nested viewports (same orientation) showOnScreen'
,
()
{
List
<
Widget
>
children
;
Future
<
Null
>
buildNestedScroller
({
WidgetTester
tester
,
ScrollController
inner
,
ScrollController
outer
})
{
return
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
200.0
,
width:
300.0
,
child:
new
ListView
(
controller:
outer
,
children:
<
Widget
>[
new
Container
(
height:
200.0
,
),
new
Container
(
height:
200.0
,
width:
300.0
,
child:
new
ListView
(
controller:
inner
,
children:
children
=
new
List
<
Widget
>.
generate
(
10
,
(
int
i
)
{
return
new
Container
(
height:
100.0
,
width:
300.0
,
child:
new
Text
(
'
$i
'
),
);
}),
),
),
new
Container
(
height:
200.0
,
)
],
),
),
),
),
);
}
testWidgets
(
'in view in inner, but not in outer'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
inner
=
new
ScrollController
();
final
ScrollController
outer
=
new
ScrollController
();
await
buildNestedScroller
(
tester:
tester
,
inner:
inner
,
outer:
outer
,
);
expect
(
outer
.
offset
,
0.0
);
expect
(
inner
.
offset
,
0.0
);
tester
.
renderObject
(
find
.
byWidget
(
children
[
0
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
inner
.
offset
,
0.0
);
expect
(
outer
.
offset
,
100.0
);
});
testWidgets
(
'not in view of neither inner nor outer'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
inner
=
new
ScrollController
();
final
ScrollController
outer
=
new
ScrollController
();
await
buildNestedScroller
(
tester:
tester
,
inner:
inner
,
outer:
outer
,
);
expect
(
outer
.
offset
,
0.0
);
expect
(
inner
.
offset
,
0.0
);
tester
.
renderObject
(
find
.
byWidget
(
children
[
4
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
inner
.
offset
,
300.0
);
expect
(
outer
.
offset
,
200.0
);
});
testWidgets
(
'in view in inner and outer'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
inner
=
new
ScrollController
(
initialScrollOffset:
200.0
);
final
ScrollController
outer
=
new
ScrollController
(
initialScrollOffset:
200.0
);
await
buildNestedScroller
(
tester:
tester
,
inner:
inner
,
outer:
outer
,
);
expect
(
outer
.
offset
,
200.0
);
expect
(
inner
.
offset
,
200.0
);
tester
.
renderObject
(
find
.
byWidget
(
children
[
2
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
outer
.
offset
,
200.0
);
expect
(
inner
.
offset
,
200.0
);
});
testWidgets
(
'inner shown in outer, but item not visible'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
inner
=
new
ScrollController
(
initialScrollOffset:
200.0
);
final
ScrollController
outer
=
new
ScrollController
(
initialScrollOffset:
200.0
);
await
buildNestedScroller
(
tester:
tester
,
inner:
inner
,
outer:
outer
,
);
expect
(
outer
.
offset
,
200.0
);
expect
(
inner
.
offset
,
200.0
);
tester
.
renderObject
(
find
.
byWidget
(
children
[
5
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
outer
.
offset
,
200.0
);
expect
(
inner
.
offset
,
400.0
);
});
testWidgets
(
'inner half shown in outer, item only visible in inner'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
inner
=
new
ScrollController
();
final
ScrollController
outer
=
new
ScrollController
(
initialScrollOffset:
100.0
);
await
buildNestedScroller
(
tester:
tester
,
inner:
inner
,
outer:
outer
,
);
expect
(
outer
.
offset
,
100.0
);
expect
(
inner
.
offset
,
0.0
);
tester
.
renderObject
(
find
.
byWidget
(
children
[
1
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
outer
.
offset
,
200.0
);
expect
(
inner
.
offset
,
0.0
);
});
});
testWidgets
(
'Viewport showOnScreen with objects larger than viewport'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
children
;
ScrollController
controller
;
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
200.0
,
child:
new
ListView
(
controller:
controller
=
new
ScrollController
(
initialScrollOffset:
300.0
),
children:
children
=
new
List
<
Widget
>.
generate
(
20
,
(
int
i
)
{
return
new
Container
(
height:
300.0
,
child:
new
Text
(
'Tile
$i
'
),
);
}),
),
),
),
),
);
expect
(
controller
.
offset
,
300.0
);
// Already aligned with leading edge, nothing happens.
tester
.
renderObject
(
find
.
byWidget
(
children
[
1
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
offset
,
300.0
);
// Above leading edge aligns trailing edges
tester
.
renderObject
(
find
.
byWidget
(
children
[
0
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
offset
,
100.0
);
// Below trailing edge aligns leading edges
tester
.
renderObject
(
find
.
byWidget
(
children
[
1
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
offset
,
300.0
);
controller
.
jumpTo
(
250.0
);
await
tester
.
pumpAndSettle
();
expect
(
controller
.
offset
,
250.0
);
// Partly visible across leading edge aligns trailing edges
tester
.
renderObject
(
find
.
byWidget
(
children
[
0
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
offset
,
100.0
);
controller
.
jumpTo
(
150.0
);
await
tester
.
pumpAndSettle
();
expect
(
controller
.
offset
,
150.0
);
// Partly visible across trailing edge aligns leading edges
tester
.
renderObject
(
find
.
byWidget
(
children
[
1
],
skipOffstage:
false
)).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
offset
,
300.0
);
});
}
packages/flutter/test/widgets/list_wheel_scroll_view_test.dart
View file @
3b9b5ace
...
...
@@ -743,4 +743,127 @@ void main() {
debugDefaultTargetPlatformOverride
=
null
;
});
});
testWidgets
(
'ListWheelScrollView getOffsetToReveal'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
outerChildren
;
final
List
<
Widget
>
innerChildren
=
new
List
<
Widget
>(
10
);
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
500.0
,
width:
300.0
,
child:
new
ListWheelScrollView
(
controller:
new
ScrollController
(
initialScrollOffset:
300.0
),
itemExtent:
100.0
,
children:
outerChildren
=
new
List
<
Widget
>.
generate
(
10
,
(
int
i
)
{
return
new
Container
(
child:
new
Center
(
child:
innerChildren
[
i
]
=
new
Container
(
height:
50.0
,
width:
50.0
,
child:
new
Text
(
'Item
$i
'
),
),
),
);
}),
),
),
),
),
);
final
RenderListWheelViewport
viewport
=
tester
.
allRenderObjects
.
firstWhere
((
RenderObject
r
)
=>
r
is
RenderListWheelViewport
);
// direct child of viewport
RenderObject
target
=
tester
.
renderObject
(
find
.
byWidget
(
outerChildren
[
5
]));
RevealedOffset
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
);
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
200.0
,
300.0
,
100.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
);
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
200.0
,
300.0
,
100.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
40.0
,
240.0
,
10.0
,
10.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
40.0
,
240.0
,
10.0
,
10.0
));
// descendant of viewport, not direct child
target
=
tester
.
renderObject
(
find
.
byWidget
(
innerChildren
[
5
]));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
);
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
125.0
,
225.0
,
50.0
,
50.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
);
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
125.0
,
225.0
,
50.0
,
50.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
165.0
,
265.0
,
10.0
,
10.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
165.0
,
265.0
,
10.0
,
10.0
));
});
testWidgets
(
'ListWheelScrollView showOnScreen'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
outerChildren
;
final
List
<
Widget
>
innerChildren
=
new
List
<
Widget
>(
10
);
ScrollController
controller
;
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
500.0
,
width:
300.0
,
child:
new
ListWheelScrollView
(
controller:
controller
=
new
ScrollController
(
initialScrollOffset:
300.0
),
itemExtent:
100.0
,
children:
outerChildren
=
new
List
<
Widget
>.
generate
(
10
,
(
int
i
)
{
return
new
Container
(
child:
new
Center
(
child:
innerChildren
[
i
]
=
new
Container
(
height:
50.0
,
width:
50.0
,
child:
new
Text
(
'Item
$i
'
),
),
),
);
}),
),
),
),
),
);
expect
(
controller
.
offset
,
300.0
);
tester
.
renderObject
(
find
.
byWidget
(
outerChildren
[
5
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
offset
,
500.0
);
tester
.
renderObject
(
find
.
byWidget
(
innerChildren
[
9
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
offset
,
900.0
);
tester
.
renderObject
(
find
.
byWidget
(
outerChildren
[
7
])).
showOnScreen
(
duration:
const
Duration
(
seconds:
2
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
tester
.
hasRunningAnimations
,
isTrue
);
expect
(
controller
.
offset
,
lessThan
(
900.0
));
expect
(
controller
.
offset
,
greaterThan
(
700.0
));
await
tester
.
pumpAndSettle
();
expect
(
controller
.
offset
,
700.0
);
});
}
packages/flutter/test/widgets/single_child_scroll_view_test.dart
View file @
3b9b5ace
...
...
@@ -5,6 +5,7 @@
import
'dart:ui'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'semantics_tester.dart'
;
...
...
@@ -341,4 +342,487 @@ void main() {
semantics
.
dispose
();
});
testWidgets
(
'SingleChildScrollView getOffsetToReveal - down'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
children
;
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
200.0
,
width:
300.0
,
child:
new
SingleChildScrollView
(
controller:
new
ScrollController
(
initialScrollOffset:
300.0
),
child:
new
Column
(
children:
children
=
new
List
<
Widget
>.
generate
(
20
,
(
int
i
)
{
return
new
Container
(
height:
100.0
,
width:
300.0
,
child:
new
Text
(
'Tile
$i
'
),
);
}),
),
),
),
),
),
);
final
RenderAbstractViewport
viewport
=
tester
.
allRenderObjects
.
firstWhere
((
RenderObject
r
)
=>
r
is
RenderAbstractViewport
);
final
RenderObject
target
=
tester
.
renderObject
(
find
.
byWidget
(
children
[
5
]));
RevealedOffset
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
);
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
0.0
,
300.0
,
100.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
);
expect
(
revealed
.
offset
,
400.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
100.0
,
300.0
,
100.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
540.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
40.0
,
0.0
,
10.0
,
10.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
350.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
40.0
,
190.0
,
10.0
,
10.0
));
});
testWidgets
(
'SingleChildScrollView getOffsetToReveal - up'
,
(
WidgetTester
tester
)
async
{
final
List
<
Widget
>
children
=
new
List
<
Widget
>.
generate
(
20
,
(
int
i
)
{
return
new
Container
(
height:
100.0
,
width:
300.0
,
child:
new
Text
(
'Tile
$i
'
),
);
});
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
200.0
,
width:
300.0
,
child:
new
SingleChildScrollView
(
controller:
new
ScrollController
(
initialScrollOffset:
300.0
),
reverse:
true
,
child:
new
Column
(
children:
children
.
reversed
.
toList
(),
),
),
),
),
),
);
final
RenderAbstractViewport
viewport
=
tester
.
allRenderObjects
.
firstWhere
((
RenderObject
r
)
=>
r
is
RenderAbstractViewport
);
final
RenderObject
target
=
tester
.
renderObject
(
find
.
byWidget
(
children
[
5
]));
RevealedOffset
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
);
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
100.0
,
300.0
,
100.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
);
expect
(
revealed
.
offset
,
400.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
0.0
,
300.0
,
100.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
550.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
40.0
,
190.0
,
10.0
,
10.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
360.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
40.0
,
0.0
,
10.0
,
10.0
));
});
testWidgets
(
'SingleChildScrollView getOffsetToReveal - right'
,
(
WidgetTester
tester
)
async
{
List
<
Widget
>
children
;
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
300.0
,
width:
200.0
,
child:
new
SingleChildScrollView
(
scrollDirection:
Axis
.
horizontal
,
controller:
new
ScrollController
(
initialScrollOffset:
300.0
),
child:
new
Row
(
children:
children
=
new
List
<
Widget
>.
generate
(
20
,
(
int
i
)
{
return
new
Container
(
height:
300.0
,
width:
100.0
,
child:
new
Text
(
'Tile
$i
'
),
);
}),
),
),
),
),
),
);
final
RenderAbstractViewport
viewport
=
tester
.
allRenderObjects
.
firstWhere
((
RenderObject
r
)
=>
r
is
RenderAbstractViewport
);
final
RenderObject
target
=
tester
.
renderObject
(
find
.
byWidget
(
children
[
5
]));
RevealedOffset
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
);
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
0.0
,
100.0
,
300.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
);
expect
(
revealed
.
offset
,
400.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
100.0
,
0.0
,
100.0
,
300.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
540.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
40.0
,
10.0
,
10.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
350.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
190.0
,
40.0
,
10.0
,
10.0
));
});
testWidgets
(
'SingleChildScrollView getOffsetToReveal - left'
,
(
WidgetTester
tester
)
async
{
final
List
<
Widget
>
children
=
new
List
<
Widget
>.
generate
(
20
,
(
int
i
)
{
return
new
Container
(
height:
300.0
,
width:
100.0
,
child:
new
Text
(
'Tile
$i
'
),
);
});
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
300.0
,
width:
200.0
,
child:
new
SingleChildScrollView
(
scrollDirection:
Axis
.
horizontal
,
reverse:
true
,
controller:
new
ScrollController
(
initialScrollOffset:
300.0
),
child:
new
Row
(
children:
children
.
reversed
.
toList
(),
),
),
),
),
),
);
final
RenderAbstractViewport
viewport
=
tester
.
allRenderObjects
.
firstWhere
((
RenderObject
r
)
=>
r
is
RenderAbstractViewport
);
final
RenderObject
target
=
tester
.
renderObject
(
find
.
byWidget
(
children
[
5
]));
RevealedOffset
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
);
expect
(
revealed
.
offset
,
500.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
100.0
,
0.0
,
100.0
,
300.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
);
expect
(
revealed
.
offset
,
400.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
0.0
,
100.0
,
300.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
0.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
550.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
190.0
,
40.0
,
10.0
,
10.0
));
revealed
=
viewport
.
getOffsetToReveal
(
target
,
1.0
,
rect:
new
Rect
.
fromLTWH
(
40.0
,
40.0
,
10.0
,
10.0
));
expect
(
revealed
.
offset
,
360.0
);
expect
(
revealed
.
rect
,
new
Rect
.
fromLTWH
(
0.0
,
40.0
,
10.0
,
10.0
));
});
testWidgets
(
'Nested SingleChildScrollView showOnScreen'
,
(
WidgetTester
tester
)
async
{
final
List
<
List
<
Widget
>>
children
=
new
List
<
List
<
Widget
>>(
10
);
ScrollController
controllerX
;
ScrollController
controllerY
;
/// Builds a gird:
///
/// <- x ->
/// 0 1 2 3 4 5 6 7 8 9
/// 0 c c c c c c c c c c
/// 1 c c c c c c c c c c
/// 2 c c c c c c c c c c
/// 3 c c c c c c c c c c y
/// 4 c c c c v v c c c c
/// 5 c c c c v v c c c c
/// 6 c c c c c c c c c c
/// 7 c c c c c c c c c c
/// 8 c c c c c c c c c c
/// 9 c c c c c c c c c c
///
/// Each c is a 100x100 container, v are containers visible in initial
/// viewport.
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
200.0
,
width:
200.0
,
child:
new
SingleChildScrollView
(
controller:
controllerY
=
new
ScrollController
(
initialScrollOffset:
400.0
),
child:
new
SingleChildScrollView
(
controller:
controllerX
=
new
ScrollController
(
initialScrollOffset:
400.0
),
scrollDirection:
Axis
.
horizontal
,
child:
new
Column
(
children:
new
List
<
Widget
>.
generate
(
10
,
(
int
y
)
{
return
new
Row
(
children:
children
[
y
]
=
new
List
<
Widget
>.
generate
(
10
,
(
int
x
)
{
return
new
Container
(
height:
100.0
,
width:
100.0
,
);
})
);
}),
),
),
),
),
),
),
);
expect
(
controllerX
.
offset
,
400.0
);
expect
(
controllerY
.
offset
,
400.0
);
// Already in viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
4
][
4
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllerX
.
offset
,
400.0
);
expect
(
controllerY
.
offset
,
400.0
);
controllerX
.
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Above viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
3
][
4
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllerX
.
offset
,
400.0
);
expect
(
controllerY
.
offset
,
300.0
);
controllerX
.
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Below viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
6
][
4
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllerX
.
offset
,
400.0
);
expect
(
controllerY
.
offset
,
500.0
);
controllerX
.
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Left of viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
4
][
3
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllerX
.
offset
,
300.0
);
expect
(
controllerY
.
offset
,
400.0
);
controllerX
.
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Right of viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
4
][
6
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllerX
.
offset
,
500.0
);
expect
(
controllerY
.
offset
,
400.0
);
controllerX
.
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Above and left of viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
3
][
3
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllerX
.
offset
,
300.0
);
expect
(
controllerY
.
offset
,
300.0
);
controllerX
.
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Below and left of viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
6
][
3
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllerX
.
offset
,
300.0
);
expect
(
controllerY
.
offset
,
500.0
);
controllerX
.
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Above and right of viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
3
][
6
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllerX
.
offset
,
500.0
);
expect
(
controllerY
.
offset
,
300.0
);
controllerX
.
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Below and right of viewport
tester
.
renderObject
(
find
.
byWidget
(
children
[
6
][
6
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
controllerX
.
offset
,
500.0
);
expect
(
controllerY
.
offset
,
500.0
);
controllerX
.
jumpTo
(
400.0
);
controllerY
.
jumpTo
(
400.0
);
await
tester
.
pumpAndSettle
();
// Below and right of viewport with animations
tester
.
renderObject
(
find
.
byWidget
(
children
[
6
][
6
])).
showOnScreen
(
duration:
const
Duration
(
seconds:
2
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
tester
.
hasRunningAnimations
,
isTrue
);
expect
(
controllerX
.
offset
,
greaterThan
(
400.0
));
expect
(
controllerX
.
offset
,
lessThan
(
500.0
));
expect
(
controllerY
.
offset
,
greaterThan
(
400.0
));
expect
(
controllerY
.
offset
,
lessThan
(
500.0
));
await
tester
.
pumpAndSettle
();
expect
(
controllerX
.
offset
,
500.0
);
expect
(
controllerY
.
offset
,
500.0
);
});
group
(
'Nested SingleChildScrollView (same orientation) showOnScreen'
,
()
{
List
<
Widget
>
children
;
Future
<
Null
>
buildNestedScroller
({
WidgetTester
tester
,
ScrollController
inner
,
ScrollController
outer
})
{
return
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Center
(
child:
Container
(
height:
200.0
,
width:
300.0
,
child:
new
SingleChildScrollView
(
controller:
outer
,
child:
new
Column
(
children:
<
Widget
>[
new
Container
(
height:
200.0
,
),
new
Container
(
height:
200.0
,
width:
300.0
,
child:
new
SingleChildScrollView
(
controller:
inner
,
child:
new
Column
(
children:
children
=
new
List
<
Widget
>.
generate
(
10
,
(
int
i
)
{
return
new
Container
(
height:
100.0
,
width:
300.0
,
child:
new
Text
(
'
$i
'
),
);
}),
),
),
),
new
Container
(
height:
200.0
,
)
],
),
),
),
),
),
);
}
testWidgets
(
'in view in inner, but not in outer'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
inner
=
new
ScrollController
();
final
ScrollController
outer
=
new
ScrollController
();
await
buildNestedScroller
(
tester:
tester
,
inner:
inner
,
outer:
outer
,
);
expect
(
outer
.
offset
,
0.0
);
expect
(
inner
.
offset
,
0.0
);
tester
.
renderObject
(
find
.
byWidget
(
children
[
0
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
inner
.
offset
,
0.0
);
expect
(
outer
.
offset
,
100.0
);
});
testWidgets
(
'not in view of neither inner nor outer'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
inner
=
new
ScrollController
();
final
ScrollController
outer
=
new
ScrollController
();
await
buildNestedScroller
(
tester:
tester
,
inner:
inner
,
outer:
outer
,
);
expect
(
outer
.
offset
,
0.0
);
expect
(
inner
.
offset
,
0.0
);
tester
.
renderObject
(
find
.
byWidget
(
children
[
5
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
inner
.
offset
,
400.0
);
expect
(
outer
.
offset
,
200.0
);
});
testWidgets
(
'in view in inner and outer'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
inner
=
new
ScrollController
(
initialScrollOffset:
200.0
);
final
ScrollController
outer
=
new
ScrollController
(
initialScrollOffset:
200.0
);
await
buildNestedScroller
(
tester:
tester
,
inner:
inner
,
outer:
outer
,
);
expect
(
outer
.
offset
,
200.0
);
expect
(
inner
.
offset
,
200.0
);
tester
.
renderObject
(
find
.
byWidget
(
children
[
2
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
outer
.
offset
,
200.0
);
expect
(
inner
.
offset
,
200.0
);
});
testWidgets
(
'inner shown in outer, but item not visible'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
inner
=
new
ScrollController
(
initialScrollOffset:
200.0
);
final
ScrollController
outer
=
new
ScrollController
(
initialScrollOffset:
200.0
);
await
buildNestedScroller
(
tester:
tester
,
inner:
inner
,
outer:
outer
,
);
expect
(
outer
.
offset
,
200.0
);
expect
(
inner
.
offset
,
200.0
);
tester
.
renderObject
(
find
.
byWidget
(
children
[
5
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
outer
.
offset
,
200.0
);
expect
(
inner
.
offset
,
400.0
);
});
testWidgets
(
'inner half shown in outer, item only visible in inner'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
inner
=
new
ScrollController
();
final
ScrollController
outer
=
new
ScrollController
(
initialScrollOffset:
100.0
);
await
buildNestedScroller
(
tester:
tester
,
inner:
inner
,
outer:
outer
,
);
expect
(
outer
.
offset
,
100.0
);
expect
(
inner
.
offset
,
0.0
);
tester
.
renderObject
(
find
.
byWidget
(
children
[
1
])).
showOnScreen
();
await
tester
.
pumpAndSettle
();
expect
(
outer
.
offset
,
200.0
);
expect
(
inner
.
offset
,
0.0
);
});
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment