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
addcb1f7
Commit
addcb1f7
authored
Feb 21, 2017
by
Adam Barth
Committed by
GitHub
Feb 21, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move RenderViewport to its own file (#8310)
It doesn't need to be in the same file as RenderSliver.
parent
58898636
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
1008 additions
and
996 deletions
+1008
-996
rendering.dart
packages/flutter/lib/rendering.dart
+1
-0
sliver.dart
packages/flutter/lib/src/rendering/sliver.dart
+0
-996
viewport.dart
packages/flutter/lib/src/rendering/viewport.dart
+1007
-0
No files found.
packages/flutter/lib/rendering.dart
View file @
addcb1f7
...
...
@@ -54,6 +54,7 @@ export 'src/rendering/table.dart';
export
'src/rendering/tweens.dart'
;
export
'src/rendering/view.dart'
;
export
'src/rendering/viewport_offset.dart'
;
export
'src/rendering/viewport.dart'
;
export
'package:flutter/foundation.dart'
show
VoidCallback
,
...
...
packages/flutter/lib/src/rendering/sliver.dart
View file @
addcb1f7
...
...
@@ -1174,1002 +1174,6 @@ abstract class RenderSliverHelpers implements RenderSliver {
}
}
// THE MAIN VIEWPORT CLASS
// Transitions from the RenderBox world to the RenderSliver world.
/// An interface for render objects that are bigger on the inside.
///
/// Some render objects, such as [RenderViewport], present a portion of their
/// content, which can be controlled by a [ViewportOffset]. This interface lets
/// the framework recognize such render objects and interact with them without
/// having specific knowledge of all the various types of viewports.
abstract
class
RenderAbstractViewport
implements
RenderObject
{
/// Returns the [RenderAbstractViewport] that most closely encloses the given
/// render object.
///
/// If the object does not have a [RenderAbstractViewport] as an ancestor,
/// this function returns null.
static
RenderAbstractViewport
of
(
RenderObject
object
)
{
while
(
object
!=
null
)
{
if
(
object
is
RenderAbstractViewport
)
return
object
;
object
=
object
.
parent
;
}
return
null
;
}
/// Returns the offset that would be needed to reveal the target render object.
///
/// The `alignment` argument describes where the target should be positioned
/// after applying the returned offset. If `alignment` is 0.0, the child must
/// be positioned as close to the leading edge of the viewport as possible. If
/// `alignment` is 1.0, the child must be positioned as close to the trailing
/// edge of the viewport as possible. If `alignment` is 0.5, the child must be
/// positioned as close to the center of the viewport as possible.
///
/// 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
);
}
typedef
RenderSliver
_Advancer
(
RenderSliver
child
);
// ///
// /// See also:
// ///
// /// - [RenderSliver], which explains more about the Sliver protocol.
// /// - [RenderBox], which explains more about the Box protocol.
// /// - [RenderSliverToBoxAdapter], which allows a [RenderBox] object to be
// /// placed inside a [RenderSliver] (the opposite of this class).
abstract
class
RenderViewportBase
<
ParentDataClass
extends
ContainerParentDataMixin
<
RenderSliver
>>
extends
RenderBox
with
ContainerRenderObjectMixin
<
RenderSliver
,
ParentDataClass
>
implements
RenderAbstractViewport
{
RenderViewportBase
({
AxisDirection
axisDirection:
AxisDirection
.
down
,
@required
ViewportOffset
offset
,
})
:
_axisDirection
=
axisDirection
,
_offset
=
offset
{
assert
(
axisDirection
!=
null
);
assert
(
offset
!=
null
);
}
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
);
_offset
=
value
;
if
(
attached
)
_offset
.
addListener
(
markNeedsLayout
);
// We need to go through layout even if the new offset has the same pixels
// value as the old offset so that we will apply our viewport and content
// dimensions.
markNeedsLayout
();
}
@override
void
attach
(
PipelineOwner
owner
)
{
super
.
attach
(
owner
);
_offset
.
addListener
(
markNeedsLayout
);
}
@override
void
detach
()
{
_offset
.
removeListener
(
markNeedsLayout
);
super
.
detach
();
}
@override
bool
get
isRepaintBoundary
=>
true
;
@protected
double
layoutOneSide
(
RenderSliver
child
,
double
scrollOffset
,
double
layoutOffset
,
double
remainingPaintExtent
,
double
mainAxisExtent
,
double
crossAxisExtent
,
GrowthDirection
growthDirection
,
_Advancer
advance
,
)
{
assert
(
scrollOffset
.
isFinite
);
assert
(
scrollOffset
>=
0.0
);
ScrollDirection
adjustedUserScrollDirection
;
switch
(
growthDirection
)
{
case
GrowthDirection
.
forward
:
adjustedUserScrollDirection
=
offset
.
userScrollDirection
;
break
;
case
GrowthDirection
.
reverse
:
switch
(
offset
.
userScrollDirection
)
{
case
ScrollDirection
.
forward
:
adjustedUserScrollDirection
=
ScrollDirection
.
reverse
;
break
;
case
ScrollDirection
.
reverse
:
adjustedUserScrollDirection
=
ScrollDirection
.
forward
;
break
;
case
ScrollDirection
.
idle
:
adjustedUserScrollDirection
=
ScrollDirection
.
idle
;
break
;
}
break
;
}
assert
(
adjustedUserScrollDirection
!=
null
);
double
maxPaintOffset
=
layoutOffset
;
double
initialLayoutOffset
=
layoutOffset
;
while
(
child
!=
null
)
{
assert
(
scrollOffset
>=
0.0
);
child
.
layout
(
new
SliverConstraints
(
axisDirection:
axisDirection
,
growthDirection:
growthDirection
,
userScrollDirection:
adjustedUserScrollDirection
,
scrollOffset:
scrollOffset
,
overlap:
maxPaintOffset
-
layoutOffset
,
remainingPaintExtent:
math
.
max
(
0.0
,
remainingPaintExtent
-
layoutOffset
+
initialLayoutOffset
),
crossAxisExtent:
crossAxisExtent
,
viewportMainAxisExtent:
mainAxisExtent
,
),
parentUsesSize:
true
);
// collect the child's objects
final
SliverGeometry
childLayoutGeometry
=
child
.
geometry
;
assert
(
childLayoutGeometry
.
debugAssertIsValid
);
// first check that there isn't a correction to apply. If there is we'll
// have to start over.
if
(
childLayoutGeometry
.
scrollOffsetCorrection
!=
0.0
)
return
childLayoutGeometry
.
scrollOffsetCorrection
;
// geometry
updateChildLayoutOffset
(
child
,
layoutOffset
,
growthDirection
);
maxPaintOffset
=
math
.
max
(
layoutOffset
+
childLayoutGeometry
.
paintExtent
,
maxPaintOffset
);
scrollOffset
-=
childLayoutGeometry
.
scrollExtent
;
layoutOffset
+=
childLayoutGeometry
.
layoutExtent
;
if
(
scrollOffset
<=
0.0
)
scrollOffset
=
0.0
;
updateOutOfBoundsData
(
growthDirection
,
childLayoutGeometry
);
// move on to the next child
child
=
advance
(
child
);
}
// we made it without a correction, whee!
return
0.0
;
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
firstChild
==
null
)
return
;
if
(
hasVisualOverflow
)
{
context
.
pushClipRect
(
needsCompositing
,
offset
,
Point
.
origin
&
size
,
paintContents
);
}
else
{
paintContents
(
context
,
offset
);
}
}
@protected
void
paintContents
(
PaintingContext
context
,
Offset
offset
)
{
for
(
RenderSliver
child
in
childrenInPaintOrder
)
{
if
(
child
.
geometry
.
visible
)
context
.
paintChild
(
child
,
offset
+
paintOffsetOf
(
child
));
}
}
@override
void
debugPaintSize
(
PaintingContext
context
,
Offset
offset
)
{
assert
(()
{
super
.
debugPaintSize
(
context
,
offset
);
final
Paint
paint
=
new
Paint
()
..
style
=
PaintingStyle
.
stroke
..
strokeWidth
=
1.0
..
color
=
const
Color
(
0xFF00FF00
);
final
Canvas
canvas
=
context
.
canvas
;
RenderSliver
child
=
firstChild
;
while
(
child
!=
null
)
{
Size
size
;
switch
(
axis
)
{
case
Axis
.
vertical
:
size
=
new
Size
(
child
.
constraints
.
crossAxisExtent
,
child
.
geometry
.
layoutExtent
);
break
;
case
Axis
.
horizontal
:
size
=
new
Size
(
child
.
geometry
.
layoutExtent
,
child
.
constraints
.
crossAxisExtent
);
break
;
}
assert
(
size
!=
null
);
canvas
.
drawRect
(((
offset
+
paintOffsetOf
(
child
))
&
size
).
deflate
(
0.5
),
paint
);
child
=
childAfter
(
child
);
}
return
true
;
});
}
@override
bool
hitTestChildren
(
HitTestResult
result
,
{
Point
position
})
{
double
mainAxisPosition
,
crossAxisPosition
;
switch
(
axis
)
{
case
Axis
.
vertical
:
mainAxisPosition
=
position
.
y
;
crossAxisPosition
=
position
.
x
;
break
;
case
Axis
.
horizontal
:
mainAxisPosition
=
position
.
x
;
crossAxisPosition
=
position
.
y
;
break
;
}
assert
(
mainAxisPosition
!=
null
);
assert
(
crossAxisPosition
!=
null
);
for
(
RenderSliver
child
in
childrenInHitTestOrder
)
{
if
(
child
.
geometry
.
visible
&&
child
.
hitTest
(
result
,
mainAxisPosition:
computeChildMainAxisPosition
(
child
,
mainAxisPosition
),
crossAxisPosition:
crossAxisPosition
))
{
return
true
;
}
}
return
false
;
}
@override
double
getOffsetToReveal
(
RenderObject
target
,
double
alignment
)
{
double
leadingScrollOffset
;
double
targetMainAxisExtent
;
RenderObject
descendant
;
if
(
target
is
RenderBox
)
{
final
RenderBox
targetBox
=
target
;
RenderBox
pivot
=
targetBox
;
while
(
pivot
.
parent
is
RenderBox
)
pivot
=
pivot
.
parent
;
assert
(
pivot
.
parent
!=
null
);
assert
(
pivot
.
parent
!=
this
);
assert
(
pivot
!=
this
);
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
GrowthDirection
growthDirection
=
pivotParent
.
constraints
.
growthDirection
;
switch
(
applyGrowthDirectionToAxisDirection
(
axisDirection
,
growthDirection
))
{
case
AxisDirection
.
up
:
leadingScrollOffset
=
pivot
.
size
.
height
-
bounds
.
bottom
;
targetMainAxisExtent
=
bounds
.
height
;
break
;
case
AxisDirection
.
right
:
leadingScrollOffset
=
bounds
.
left
;
targetMainAxisExtent
=
bounds
.
width
;
break
;
case
AxisDirection
.
down
:
leadingScrollOffset
=
bounds
.
top
;
targetMainAxisExtent
=
bounds
.
height
;
break
;
case
AxisDirection
.
left
:
leadingScrollOffset
=
pivot
.
size
.
width
-
bounds
.
right
;
targetMainAxisExtent
=
bounds
.
width
;
break
;
}
descendant
=
pivot
;
}
else
if
(
target
is
RenderSliver
)
{
final
RenderSliver
targetSliver
=
target
;
leadingScrollOffset
=
0.0
;
targetMainAxisExtent
=
targetSliver
.
geometry
.
scrollExtent
;
descendant
=
targetSliver
;
}
else
{
return
offset
.
pixels
;
}
// The child will be the topmost object before we get to the viewport.
RenderObject
child
=
descendant
;
while
(
child
.
parent
is
RenderSliver
)
{
final
RenderSliver
parent
=
child
.
parent
;
leadingScrollOffset
+=
parent
.
childScrollOffset
(
child
);
child
=
parent
;
}
assert
(
child
.
parent
==
this
);
assert
(
child
is
RenderSliver
);
final
RenderSliver
sliver
=
child
;
leadingScrollOffset
=
scrollOffsetOf
(
sliver
,
leadingScrollOffset
);
double
mainAxisExtent
;
switch
(
axis
)
{
case
Axis
.
horizontal
:
mainAxisExtent
=
size
.
width
;
break
;
case
Axis
.
vertical
:
mainAxisExtent
=
size
.
height
;
break
;
}
return
leadingScrollOffset
-
(
mainAxisExtent
-
targetMainAxisExtent
)
*
alignment
;
}
@protected
Offset
computeAbsolutePaintOffset
(
RenderSliver
child
,
double
layoutOffset
,
GrowthDirection
growthDirection
)
{
assert
(
hasSize
);
// this is only usable once we have a size
assert
(
axisDirection
!=
null
);
assert
(
growthDirection
!=
null
);
assert
(
child
!=
null
);
assert
(
child
.
geometry
!=
null
);
switch
(
applyGrowthDirectionToAxisDirection
(
axisDirection
,
growthDirection
))
{
case
AxisDirection
.
up
:
return
new
Offset
(
0.0
,
size
.
height
-
(
layoutOffset
+
child
.
geometry
.
paintExtent
));
case
AxisDirection
.
right
:
return
new
Offset
(
layoutOffset
,
0.0
);
case
AxisDirection
.
down
:
return
new
Offset
(
0.0
,
layoutOffset
);
case
AxisDirection
.
left
:
return
new
Offset
(
size
.
width
-
(
layoutOffset
+
child
.
geometry
.
paintExtent
),
0.0
);
}
return
null
;
}
// TODO(ianh): semantics - shouldn't walk the invisible children
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'
$axisDirection
'
);
description
.
add
(
'offset:
$offset
'
);
}
@override
String
debugDescribeChildren
(
String
prefix
)
{
if
(
firstChild
==
null
)
return
'
$prefix
\n
'
;
int
count
=
indexOfFirstChild
();
String
result
=
'
$prefix
\
u2502
\n
'
;
RenderSliver
child
=
firstChild
;
while
(
child
!=
lastChild
)
{
result
+=
'
${child.toStringDeep("$prefix \u251C\u2500${labelForChild(count)}
: ", "
$prefix
\
u2502")}'
;
count
+=
1
;
child
=
childAfter
(
child
);
}
assert
(
child
==
lastChild
);
result
+=
'
${child.toStringDeep("$prefix \u2514\u2500${labelForChild(count)}
: ", "
$prefix
")}'
;
return
result
;
}
// API TO BE IMPLEMENTED BY SUBCLASSES
// setupParentData
// performLayout (and optionally sizedByParent and performResize)
@protected
bool
get
hasVisualOverflow
;
@protected
void
updateOutOfBoundsData
(
GrowthDirection
growthDirection
,
SliverGeometry
childLayoutGeometry
);
@protected
void
updateChildLayoutOffset
(
RenderSliver
child
,
double
layoutOffset
,
GrowthDirection
growthDirection
);
@protected
Offset
paintOffsetOf
(
RenderSliver
child
);
@protected
double
scrollOffsetOf
(
RenderSliver
child
,
double
scrollOffset
);
// applyPaintTransform
/// Converts the `parentMainAxisPosition` into the child's coordinate system.
///
/// The `parentMainAxisPosition` is a distance from the top edge (for vertical
/// viewports) or left edge (for horizontal viewports) of the viewport bounds.
/// This describes a line, perpendicular to the viewport's main axis, heretofor
/// known as the target line.
///
/// The child's coordinate system's origin in the main axis is at the leading
/// edge of the given child, as given by the child's
/// [SliverConstraints.axisDirection] and [SliverConstraints.growthDirection].
///
/// This method returns the distance from the leading edge of the given child to
/// the target line described above.
///
/// (The `parentMainAxisPosition` is not from the leading edge of the
/// viewport, it's always the top or left edge.)
@protected
double
computeChildMainAxisPosition
(
RenderSliver
child
,
double
parentMainAxisPosition
);
@protected
int
indexOfFirstChild
();
@protected
String
labelForChild
(
int
index
);
/// Provides an iterable that walks the children of the viewport, in the order
/// that they should be painted.
///
/// This should be the reverse order of [childrenInHitTestOrder].
@protected
Iterable
<
RenderSliver
>
get
childrenInPaintOrder
;
/// Provides an iterable that walks the children of the viewport, in the order
/// that hit-testing should use.
///
/// This should be the reverse order of [childrenInPaintOrder].
@protected
Iterable
<
RenderSliver
>
get
childrenInHitTestOrder
;
}
// ///
// /// See also:
// ///
// /// - [RenderSliver], which explains more about the Sliver protocol.
// /// - [RenderBox], which explains more about the Box protocol.
// /// - [RenderSliverToBoxAdapter], which allows a [RenderBox] object to be
// /// placed inside a [RenderSliver] (the opposite of this class).
// /// - [RenderShrinkWrappingViewport], a variant of [RenderViewport] that
// /// shrink-wraps its contents along the main axis.
class
RenderViewport
extends
RenderViewportBase
<
SliverPhysicalContainerParentData
>
{
/// Creates a viewport for [RenderSliver] objects.
///
/// If the [center] is not specified, then the first child in the `children`
/// list, if any, is used.
///
/// The [offset] must be specified. For testing purposes, consider passing a
/// [new ViewportOffset.zero] or [new ViewportOffset.fixed].
RenderViewport
({
AxisDirection
axisDirection:
AxisDirection
.
down
,
@required
ViewportOffset
offset
,
double
anchor:
0.0
,
List
<
RenderSliver
>
children
,
RenderSliver
center
,
})
:
_anchor
=
anchor
,
_center
=
center
,
super
(
axisDirection:
axisDirection
,
offset:
offset
)
{
assert
(
anchor
!=
null
);
assert
(
anchor
>=
0.0
&&
anchor
<=
1.0
);
addAll
(
children
);
if
(
center
==
null
&&
firstChild
!=
null
)
_center
=
firstChild
;
}
@override
void
setupParentData
(
RenderObject
child
)
{
if
(
child
.
parentData
is
!
SliverPhysicalContainerParentData
)
child
.
parentData
=
new
SliverPhysicalContainerParentData
();
}
double
get
anchor
=>
_anchor
;
double
_anchor
;
set
anchor
(
double
value
)
{
assert
(
value
!=
null
);
assert
(
value
>=
0.0
&&
value
<=
1.0
);
if
(
value
==
_anchor
)
return
;
_anchor
=
value
;
markNeedsLayout
();
}
RenderSliver
get
center
=>
_center
;
RenderSliver
_center
;
set
center
(
RenderSliver
value
)
{
if
(
value
==
_center
)
return
;
_center
=
value
;
markNeedsLayout
();
}
@override
bool
get
sizedByParent
=>
true
;
@override
void
performResize
()
{
assert
(
constraints
.
hasBoundedHeight
&&
constraints
.
hasBoundedWidth
);
size
=
constraints
.
biggest
;
// We ignore the return value of applyViewportDimension below because we are
// going to go through performLayout next regardless.
switch
(
axis
)
{
case
Axis
.
vertical
:
offset
.
applyViewportDimension
(
size
.
height
);
break
;
case
Axis
.
horizontal
:
offset
.
applyViewportDimension
(
size
.
width
);
break
;
}
}
static
const
int
_kMaxLayoutCycles
=
10
;
// Out-of-band data computed during layout.
double
_minScrollExtent
;
double
_maxScrollExtent
;
bool
_hasVisualOverflow
=
false
;
@override
void
performLayout
()
{
if
(
center
==
null
)
{
assert
(
firstChild
==
null
);
_minScrollExtent
=
0.0
;
_maxScrollExtent
=
0.0
;
_hasVisualOverflow
=
false
;
offset
.
applyContentDimensions
(
0.0
,
0.0
);
return
;
}
assert
(
center
.
parent
==
this
);
double
mainAxisExtent
;
double
crossAxisExtent
;
switch
(
axis
)
{
case
Axis
.
vertical
:
mainAxisExtent
=
size
.
height
;
crossAxisExtent
=
size
.
width
;
break
;
case
Axis
.
horizontal
:
mainAxisExtent
=
size
.
width
;
crossAxisExtent
=
size
.
height
;
break
;
}
final
double
centerOffsetAdjustment
=
center
.
centerOffsetAdjustment
;
double
correction
;
int
count
=
0
;
do
{
assert
(
offset
.
pixels
!=
null
);
correction
=
_attemptLayout
(
mainAxisExtent
,
crossAxisExtent
,
offset
.
pixels
+
centerOffsetAdjustment
);
if
(
correction
!=
0.0
)
{
offset
.
correctBy
(
correction
);
}
else
{
if
(
offset
.
applyContentDimensions
(
math
.
min
(
0.0
,
_minScrollExtent
+
mainAxisExtent
*
anchor
),
math
.
max
(
0.0
,
_maxScrollExtent
-
mainAxisExtent
*
(
1.0
-
anchor
)),
))
break
;
}
count
+=
1
;
}
while
(
count
<
_kMaxLayoutCycles
);
assert
(()
{
if
(
count
>=
_kMaxLayoutCycles
)
{
assert
(
count
!=
1
);
throw
new
FlutterError
(
'A RenderViewport exceeded its maximum number of layout cycles.
\n
'
'RenderViewport render objects, during layout, can retry if either their '
'slivers or their ViewportOffset decide that the offset should be corrected '
'to take into account information collected during that layout.
\n
'
'In the case of this RenderViewport object, however, this happened
$count
'
'times and still there was no consensus on the scroll offset. This usually '
'indicates a bug. Specifically, it means that one of the following three '
'problems is being experienced by the RenderViewport object:
\n
'
' * One of the RenderSliver children or the ViewportOffset have a bug such'
' that they always think that they need to correct the offset regardless.
\n
'
' * Some combination of the RenderSliver children and the ViewportOffset'
' have a bad interaction such that one applies a correction then another'
' applies a reverse correction, leading to an infinite loop of corrections.
\n
'
' * There is a pathological case that would eventually resolve, but it is'
' so complicated that it cannot be resolved in any reasonable number of'
' layout passes.'
);
}
return
true
;
});
}
double
_attemptLayout
(
double
mainAxisExtent
,
double
crossAxisExtent
,
double
correctedOffset
)
{
assert
(!
mainAxisExtent
.
isNaN
);
assert
(
mainAxisExtent
>=
0.0
);
assert
(
crossAxisExtent
.
isFinite
);
assert
(
crossAxisExtent
>=
0.0
);
assert
(
correctedOffset
.
isFinite
);
_minScrollExtent
=
0.0
;
_maxScrollExtent
=
0.0
;
_hasVisualOverflow
=
false
;
// centerOffset is the offset from the leading edge of the RenderViewport
// to the zero scroll offset (the line between the forward slivers and the
// reverse slivers). The other two are that, but clamped to the visible
// region of the viewport.
final
double
centerOffset
=
mainAxisExtent
*
anchor
-
correctedOffset
;
final
double
clampedForwardCenter
=
math
.
max
(
0.0
,
math
.
min
(
mainAxisExtent
,
centerOffset
));
final
double
clampedReverseCenter
=
math
.
max
(
0.0
,
math
.
min
(
mainAxisExtent
,
mainAxisExtent
-
centerOffset
));
// negative scroll offsets
double
result
=
layoutOneSide
(
childBefore
(
center
),
math
.
max
(
mainAxisExtent
,
mainAxisExtent
*
anchor
-
correctedOffset
)
-
mainAxisExtent
,
clampedReverseCenter
,
clampedForwardCenter
,
mainAxisExtent
,
crossAxisExtent
,
GrowthDirection
.
reverse
,
childBefore
,
);
if
(
result
!=
0.0
)
return
-
result
;
// positive scroll offsets
return
layoutOneSide
(
center
,
math
.
max
(
0.0
,
correctedOffset
-
mainAxisExtent
*
anchor
),
clampedForwardCenter
,
clampedReverseCenter
,
mainAxisExtent
,
crossAxisExtent
,
GrowthDirection
.
forward
,
childAfter
,
);
}
@override
bool
get
hasVisualOverflow
=>
_hasVisualOverflow
;
@override
void
updateOutOfBoundsData
(
GrowthDirection
growthDirection
,
SliverGeometry
childLayoutGeometry
)
{
switch
(
growthDirection
)
{
case
GrowthDirection
.
forward
:
_maxScrollExtent
+=
childLayoutGeometry
.
scrollExtent
;
break
;
case
GrowthDirection
.
reverse
:
_minScrollExtent
-=
childLayoutGeometry
.
scrollExtent
;
break
;
}
if
(
childLayoutGeometry
.
hasVisualOverflow
)
_hasVisualOverflow
=
true
;
}
@override
void
updateChildLayoutOffset
(
RenderSliver
child
,
double
layoutOffset
,
GrowthDirection
growthDirection
)
{
final
SliverPhysicalParentData
childParentData
=
child
.
parentData
;
childParentData
.
paintOffset
=
computeAbsolutePaintOffset
(
child
,
layoutOffset
,
growthDirection
);
}
@override
Offset
paintOffsetOf
(
RenderSliver
child
)
{
final
SliverPhysicalParentData
childParentData
=
child
.
parentData
;
return
childParentData
.
paintOffset
;
}
@override
double
scrollOffsetOf
(
RenderSliver
child
,
double
scrollOffsetWithinChild
)
{
assert
(
child
.
parent
==
this
);
final
GrowthDirection
growthDirection
=
child
.
constraints
.
growthDirection
;
assert
(
growthDirection
!=
null
);
switch
(
growthDirection
)
{
case
GrowthDirection
.
forward
:
double
scrollOffsetToChild
=
0.0
;
RenderSliver
current
=
center
;
while
(
current
!=
child
)
{
scrollOffsetToChild
+=
current
.
geometry
.
scrollExtent
;
current
=
childAfter
(
current
);
}
return
scrollOffsetToChild
+
scrollOffsetWithinChild
;
case
GrowthDirection
.
reverse
:
double
scrollOffsetToChild
=
0.0
;
RenderSliver
current
=
childBefore
(
center
);
while
(
current
!=
child
)
{
scrollOffsetToChild
-=
current
.
geometry
.
scrollExtent
;
current
=
childBefore
(
current
);
}
return
scrollOffsetToChild
-
scrollOffsetWithinChild
;
}
return
null
;
}
@override
void
applyPaintTransform
(
RenderObject
child
,
Matrix4
transform
)
{
assert
(
child
!=
null
);
final
SliverPhysicalParentData
childParentData
=
child
.
parentData
;
childParentData
.
applyPaintTransform
(
transform
);
}
@override
double
computeChildMainAxisPosition
(
RenderSliver
child
,
double
parentMainAxisPosition
)
{
assert
(
child
!=
null
);
assert
(
child
.
constraints
!=
null
);
final
SliverPhysicalParentData
childParentData
=
child
.
parentData
;
switch
(
applyGrowthDirectionToAxisDirection
(
child
.
constraints
.
axisDirection
,
child
.
constraints
.
growthDirection
))
{
case
AxisDirection
.
down
:
return
parentMainAxisPosition
-
childParentData
.
paintOffset
.
dy
;
case
AxisDirection
.
right
:
return
parentMainAxisPosition
-
childParentData
.
paintOffset
.
dx
;
case
AxisDirection
.
up
:
return
child
.
geometry
.
paintExtent
-
(
parentMainAxisPosition
-
childParentData
.
paintOffset
.
dy
);
case
AxisDirection
.
left
:
return
child
.
geometry
.
paintExtent
-
(
parentMainAxisPosition
-
childParentData
.
paintOffset
.
dx
);
}
return
0.0
;
}
@override
int
indexOfFirstChild
()
{
assert
(
center
!=
null
);
assert
(
center
.
parent
==
this
);
assert
(
firstChild
!=
null
);
int
count
=
0
;
RenderSliver
child
=
center
;
while
(
child
!=
firstChild
)
{
count
-=
1
;
child
=
childBefore
(
child
);
}
return
count
;
}
@override
String
labelForChild
(
int
index
)
{
if
(
index
==
0
)
return
'center child'
;
return
'child
$index
'
;
}
@override
Iterable
<
RenderSliver
>
get
childrenInPaintOrder
sync
*
{
if
(
firstChild
==
null
)
return
;
RenderSliver
child
=
firstChild
;
while
(
child
!=
center
)
{
yield
child
;
child
=
childAfter
(
child
);
}
child
=
lastChild
;
while
(
true
)
{
yield
child
;
if
(
child
==
center
)
return
;
child
=
childBefore
(
child
);
}
}
@override
Iterable
<
RenderSliver
>
get
childrenInHitTestOrder
sync
*
{
if
(
firstChild
==
null
)
return
;
RenderSliver
child
=
center
;
while
(
child
!=
null
)
{
yield
child
;
child
=
childAfter
(
child
);
}
child
=
childBefore
(
center
);
while
(
child
!=
null
)
{
yield
child
;
child
=
childBefore
(
child
);
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'anchor:
$anchor
'
);
}
}
// ///
// /// See also:
// ///
// /// - [RenderViewport], a viewport that does not shrink-wrap its contents
// /// - [RenderSliver], which explains more about the Sliver protocol.
// /// - [RenderBox], which explains more about the Box protocol.
// /// - [RenderSliverToBoxAdapter], which allows a [RenderBox] object to be
// /// placed inside a [RenderSliver] (the opposite of this class).
class
RenderShrinkWrappingViewport
extends
RenderViewportBase
<
SliverLogicalContainerParentData
>
{
/// Creates a viewport (for [RenderSliver] objects) that shrink-wraps its
/// contents.
///
/// The [offset] must be specified. For testing purposes, consider passing a
/// [new ViewportOffset.zero] or [new ViewportOffset.fixed].
RenderShrinkWrappingViewport
({
AxisDirection
axisDirection:
AxisDirection
.
down
,
@required
ViewportOffset
offset
,
List
<
RenderSliver
>
children
,
})
:
super
(
axisDirection:
axisDirection
,
offset:
offset
)
{
addAll
(
children
);
}
@override
void
setupParentData
(
RenderObject
child
)
{
if
(
child
.
parentData
is
!
SliverLogicalContainerParentData
)
child
.
parentData
=
new
SliverLogicalContainerParentData
();
}
// Out-of-band data computed during layout.
double
_maxScrollExtent
;
double
_shrinkWrapExtent
;
bool
_hasVisualOverflow
=
false
;
@override
void
performLayout
()
{
if
(
firstChild
==
null
)
{
switch
(
axis
)
{
case
Axis
.
vertical
:
assert
(
constraints
.
hasBoundedWidth
);
size
=
new
Size
(
constraints
.
maxWidth
,
constraints
.
minHeight
);
break
;
case
Axis
.
horizontal
:
assert
(
constraints
.
hasBoundedHeight
);
size
=
new
Size
(
constraints
.
minWidth
,
constraints
.
maxHeight
);
break
;
}
offset
.
applyViewportDimension
(
0.0
);
_maxScrollExtent
=
0.0
;
_shrinkWrapExtent
=
0.0
;
_hasVisualOverflow
=
false
;
offset
.
applyContentDimensions
(
0.0
,
0.0
);
return
;
}
double
mainAxisExtent
;
double
crossAxisExtent
;
switch
(
axis
)
{
case
Axis
.
vertical
:
assert
(
constraints
.
hasBoundedWidth
);
mainAxisExtent
=
constraints
.
maxHeight
;
crossAxisExtent
=
constraints
.
maxWidth
;
break
;
case
Axis
.
horizontal
:
assert
(
constraints
.
hasBoundedHeight
);
mainAxisExtent
=
constraints
.
maxWidth
;
crossAxisExtent
=
constraints
.
maxHeight
;
break
;
}
double
correction
;
double
effectiveExtent
;
do
{
assert
(
offset
.
pixels
!=
null
);
correction
=
_attemptLayout
(
mainAxisExtent
,
crossAxisExtent
,
offset
.
pixels
);
if
(
correction
!=
0.0
)
{
offset
.
correctBy
(
correction
);
}
else
{
switch
(
axis
)
{
case
Axis
.
vertical
:
effectiveExtent
=
constraints
.
constrainHeight
(
_shrinkWrapExtent
);
break
;
case
Axis
.
horizontal
:
effectiveExtent
=
constraints
.
constrainWidth
(
_shrinkWrapExtent
);
break
;
}
final
bool
didAcceptViewportDimension
=
offset
.
applyViewportDimension
(
effectiveExtent
);
final
bool
didAcceptContentDimension
=
offset
.
applyContentDimensions
(
0.0
,
math
.
max
(
0.0
,
_maxScrollExtent
-
effectiveExtent
));
if
(
didAcceptViewportDimension
&&
didAcceptContentDimension
)
break
;
}
}
while
(
true
);
switch
(
axis
)
{
case
Axis
.
vertical
:
size
=
constraints
.
constrainDimensions
(
crossAxisExtent
,
effectiveExtent
);
break
;
case
Axis
.
horizontal
:
size
=
constraints
.
constrainDimensions
(
effectiveExtent
,
crossAxisExtent
);
break
;
}
}
double
_attemptLayout
(
double
mainAxisExtent
,
double
crossAxisExtent
,
double
correctedOffset
)
{
assert
(!
mainAxisExtent
.
isNaN
);
assert
(
mainAxisExtent
>=
0.0
);
assert
(
crossAxisExtent
.
isFinite
);
assert
(
crossAxisExtent
>=
0.0
);
assert
(
correctedOffset
.
isFinite
);
_maxScrollExtent
=
0.0
;
_shrinkWrapExtent
=
0.0
;
_hasVisualOverflow
=
false
;
return
layoutOneSide
(
firstChild
,
math
.
max
(
0.0
,
correctedOffset
),
0.0
,
mainAxisExtent
,
mainAxisExtent
,
crossAxisExtent
,
GrowthDirection
.
forward
,
childAfter
,
);
}
@override
bool
get
hasVisualOverflow
=>
_hasVisualOverflow
;
@override
void
updateOutOfBoundsData
(
GrowthDirection
growthDirection
,
SliverGeometry
childLayoutGeometry
)
{
assert
(
growthDirection
==
GrowthDirection
.
forward
);
_maxScrollExtent
+=
childLayoutGeometry
.
scrollExtent
;
if
(
childLayoutGeometry
.
hasVisualOverflow
)
_hasVisualOverflow
=
true
;
_shrinkWrapExtent
+=
childLayoutGeometry
.
maxPaintExtent
;
}
@override
void
updateChildLayoutOffset
(
RenderSliver
child
,
double
layoutOffset
,
GrowthDirection
growthDirection
)
{
assert
(
growthDirection
==
GrowthDirection
.
forward
);
final
SliverLogicalParentData
childParentData
=
child
.
parentData
;
childParentData
.
layoutOffset
=
layoutOffset
;
}
@override
Offset
paintOffsetOf
(
RenderSliver
child
)
{
final
SliverLogicalParentData
childParentData
=
child
.
parentData
;
return
computeAbsolutePaintOffset
(
child
,
childParentData
.
layoutOffset
,
GrowthDirection
.
forward
);
}
@override
double
scrollOffsetOf
(
RenderSliver
child
,
double
scrollOffsetWithinChild
)
{
assert
(
child
.
parent
==
this
);
assert
(
child
.
constraints
.
growthDirection
==
GrowthDirection
.
forward
);
double
scrollOffsetToChild
=
0.0
;
RenderSliver
current
=
firstChild
;
while
(
current
!=
child
)
{
scrollOffsetToChild
+=
current
.
geometry
.
scrollExtent
;
current
=
childAfter
(
current
);
}
return
scrollOffsetToChild
+
scrollOffsetWithinChild
;
}
@override
void
applyPaintTransform
(
RenderObject
child
,
Matrix4
transform
)
{
assert
(
child
!=
null
);
final
Offset
offset
=
paintOffsetOf
(
child
);
transform
.
translate
(
offset
.
dx
,
offset
.
dy
);
}
@override
double
computeChildMainAxisPosition
(
RenderSliver
child
,
double
parentMainAxisPosition
)
{
assert
(
child
!=
null
);
assert
(
child
.
constraints
!=
null
);
assert
(
hasSize
);
final
SliverLogicalParentData
childParentData
=
child
.
parentData
;
switch
(
applyGrowthDirectionToAxisDirection
(
child
.
constraints
.
axisDirection
,
child
.
constraints
.
growthDirection
))
{
case
AxisDirection
.
down
:
case
AxisDirection
.
right
:
return
parentMainAxisPosition
-
childParentData
.
layoutOffset
;
case
AxisDirection
.
up
:
return
(
size
.
height
-
parentMainAxisPosition
)
-
childParentData
.
layoutOffset
;
case
AxisDirection
.
left
:
return
(
size
.
width
-
parentMainAxisPosition
)
-
childParentData
.
layoutOffset
;
}
return
0.0
;
}
@override
int
indexOfFirstChild
()
=>
0
;
@override
String
labelForChild
(
int
index
)
=>
'child
$index
'
;
@override
Iterable
<
RenderSliver
>
get
childrenInPaintOrder
sync
*
{
RenderSliver
child
=
firstChild
;
while
(
child
!=
null
)
{
yield
child
;
child
=
childAfter
(
child
);
}
}
@override
Iterable
<
RenderSliver
>
get
childrenInHitTestOrder
sync
*
{
RenderSliver
child
=
lastChild
;
while
(
child
!=
null
)
{
yield
child
;
child
=
childBefore
(
child
);
}
}
}
// ADAPTER FOR RENDER BOXES INSIDE SLIVERS
// Transitions from the RenderSliver world to the RenderBox world.
...
...
packages/flutter/lib/src/rendering/viewport.dart
0 → 100644
View file @
addcb1f7
// 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
'dart:math'
as
math
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:meta/meta.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
import
'box.dart'
;
import
'binding.dart'
;
import
'object.dart'
;
import
'sliver.dart'
;
import
'viewport_offset.dart'
;
/// An interface for render objects that are bigger on the inside.
///
/// Some render objects, such as [RenderViewport], present a portion of their
/// content, which can be controlled by a [ViewportOffset]. This interface lets
/// the framework recognize such render objects and interact with them without
/// having specific knowledge of all the various types of viewports.
abstract
class
RenderAbstractViewport
implements
RenderObject
{
/// Returns the [RenderAbstractViewport] that most closely encloses the given
/// render object.
///
/// If the object does not have a [RenderAbstractViewport] as an ancestor,
/// this function returns null.
static
RenderAbstractViewport
of
(
RenderObject
object
)
{
while
(
object
!=
null
)
{
if
(
object
is
RenderAbstractViewport
)
return
object
;
object
=
object
.
parent
;
}
return
null
;
}
/// Returns the offset that would be needed to reveal the target render object.
///
/// The `alignment` argument describes where the target should be positioned
/// after applying the returned offset. If `alignment` is 0.0, the child must
/// be positioned as close to the leading edge of the viewport as possible. If
/// `alignment` is 1.0, the child must be positioned as close to the trailing
/// edge of the viewport as possible. If `alignment` is 0.5, the child must be
/// positioned as close to the center of the viewport as possible.
///
/// 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
);
}
typedef
RenderSliver
_Advancer
(
RenderSliver
child
);
// ///
// /// See also:
// ///
// /// - [RenderSliver], which explains more about the Sliver protocol.
// /// - [RenderBox], which explains more about the Box protocol.
// /// - [RenderSliverToBoxAdapter], which allows a [RenderBox] object to be
// /// placed inside a [RenderSliver] (the opposite of this class).
abstract
class
RenderViewportBase
<
ParentDataClass
extends
ContainerParentDataMixin
<
RenderSliver
>>
extends
RenderBox
with
ContainerRenderObjectMixin
<
RenderSliver
,
ParentDataClass
>
implements
RenderAbstractViewport
{
RenderViewportBase
({
AxisDirection
axisDirection:
AxisDirection
.
down
,
@required
ViewportOffset
offset
,
})
:
_axisDirection
=
axisDirection
,
_offset
=
offset
{
assert
(
axisDirection
!=
null
);
assert
(
offset
!=
null
);
}
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
);
_offset
=
value
;
if
(
attached
)
_offset
.
addListener
(
markNeedsLayout
);
// We need to go through layout even if the new offset has the same pixels
// value as the old offset so that we will apply our viewport and content
// dimensions.
markNeedsLayout
();
}
@override
void
attach
(
PipelineOwner
owner
)
{
super
.
attach
(
owner
);
_offset
.
addListener
(
markNeedsLayout
);
}
@override
void
detach
()
{
_offset
.
removeListener
(
markNeedsLayout
);
super
.
detach
();
}
@override
bool
get
isRepaintBoundary
=>
true
;
@protected
double
layoutOneSide
(
RenderSliver
child
,
double
scrollOffset
,
double
layoutOffset
,
double
remainingPaintExtent
,
double
mainAxisExtent
,
double
crossAxisExtent
,
GrowthDirection
growthDirection
,
_Advancer
advance
,
)
{
assert
(
scrollOffset
.
isFinite
);
assert
(
scrollOffset
>=
0.0
);
ScrollDirection
adjustedUserScrollDirection
;
switch
(
growthDirection
)
{
case
GrowthDirection
.
forward
:
adjustedUserScrollDirection
=
offset
.
userScrollDirection
;
break
;
case
GrowthDirection
.
reverse
:
switch
(
offset
.
userScrollDirection
)
{
case
ScrollDirection
.
forward
:
adjustedUserScrollDirection
=
ScrollDirection
.
reverse
;
break
;
case
ScrollDirection
.
reverse
:
adjustedUserScrollDirection
=
ScrollDirection
.
forward
;
break
;
case
ScrollDirection
.
idle
:
adjustedUserScrollDirection
=
ScrollDirection
.
idle
;
break
;
}
break
;
}
assert
(
adjustedUserScrollDirection
!=
null
);
double
maxPaintOffset
=
layoutOffset
;
double
initialLayoutOffset
=
layoutOffset
;
while
(
child
!=
null
)
{
assert
(
scrollOffset
>=
0.0
);
child
.
layout
(
new
SliverConstraints
(
axisDirection:
axisDirection
,
growthDirection:
growthDirection
,
userScrollDirection:
adjustedUserScrollDirection
,
scrollOffset:
scrollOffset
,
overlap:
maxPaintOffset
-
layoutOffset
,
remainingPaintExtent:
math
.
max
(
0.0
,
remainingPaintExtent
-
layoutOffset
+
initialLayoutOffset
),
crossAxisExtent:
crossAxisExtent
,
viewportMainAxisExtent:
mainAxisExtent
,
),
parentUsesSize:
true
);
// collect the child's objects
final
SliverGeometry
childLayoutGeometry
=
child
.
geometry
;
assert
(
childLayoutGeometry
.
debugAssertIsValid
);
// first check that there isn't a correction to apply. If there is we'll
// have to start over.
if
(
childLayoutGeometry
.
scrollOffsetCorrection
!=
0.0
)
return
childLayoutGeometry
.
scrollOffsetCorrection
;
// geometry
updateChildLayoutOffset
(
child
,
layoutOffset
,
growthDirection
);
maxPaintOffset
=
math
.
max
(
layoutOffset
+
childLayoutGeometry
.
paintExtent
,
maxPaintOffset
);
scrollOffset
-=
childLayoutGeometry
.
scrollExtent
;
layoutOffset
+=
childLayoutGeometry
.
layoutExtent
;
if
(
scrollOffset
<=
0.0
)
scrollOffset
=
0.0
;
updateOutOfBoundsData
(
growthDirection
,
childLayoutGeometry
);
// move on to the next child
child
=
advance
(
child
);
}
// we made it without a correction, whee!
return
0.0
;
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
firstChild
==
null
)
return
;
if
(
hasVisualOverflow
)
{
context
.
pushClipRect
(
needsCompositing
,
offset
,
Point
.
origin
&
size
,
paintContents
);
}
else
{
paintContents
(
context
,
offset
);
}
}
@protected
void
paintContents
(
PaintingContext
context
,
Offset
offset
)
{
for
(
RenderSliver
child
in
childrenInPaintOrder
)
{
if
(
child
.
geometry
.
visible
)
context
.
paintChild
(
child
,
offset
+
paintOffsetOf
(
child
));
}
}
@override
void
debugPaintSize
(
PaintingContext
context
,
Offset
offset
)
{
assert
(()
{
super
.
debugPaintSize
(
context
,
offset
);
final
Paint
paint
=
new
Paint
()
..
style
=
PaintingStyle
.
stroke
..
strokeWidth
=
1.0
..
color
=
const
Color
(
0xFF00FF00
);
final
Canvas
canvas
=
context
.
canvas
;
RenderSliver
child
=
firstChild
;
while
(
child
!=
null
)
{
Size
size
;
switch
(
axis
)
{
case
Axis
.
vertical
:
size
=
new
Size
(
child
.
constraints
.
crossAxisExtent
,
child
.
geometry
.
layoutExtent
);
break
;
case
Axis
.
horizontal
:
size
=
new
Size
(
child
.
geometry
.
layoutExtent
,
child
.
constraints
.
crossAxisExtent
);
break
;
}
assert
(
size
!=
null
);
canvas
.
drawRect
(((
offset
+
paintOffsetOf
(
child
))
&
size
).
deflate
(
0.5
),
paint
);
child
=
childAfter
(
child
);
}
return
true
;
});
}
@override
bool
hitTestChildren
(
HitTestResult
result
,
{
Point
position
})
{
double
mainAxisPosition
,
crossAxisPosition
;
switch
(
axis
)
{
case
Axis
.
vertical
:
mainAxisPosition
=
position
.
y
;
crossAxisPosition
=
position
.
x
;
break
;
case
Axis
.
horizontal
:
mainAxisPosition
=
position
.
x
;
crossAxisPosition
=
position
.
y
;
break
;
}
assert
(
mainAxisPosition
!=
null
);
assert
(
crossAxisPosition
!=
null
);
for
(
RenderSliver
child
in
childrenInHitTestOrder
)
{
if
(
child
.
geometry
.
visible
&&
child
.
hitTest
(
result
,
mainAxisPosition:
computeChildMainAxisPosition
(
child
,
mainAxisPosition
),
crossAxisPosition:
crossAxisPosition
))
{
return
true
;
}
}
return
false
;
}
@override
double
getOffsetToReveal
(
RenderObject
target
,
double
alignment
)
{
double
leadingScrollOffset
;
double
targetMainAxisExtent
;
RenderObject
descendant
;
if
(
target
is
RenderBox
)
{
final
RenderBox
targetBox
=
target
;
RenderBox
pivot
=
targetBox
;
while
(
pivot
.
parent
is
RenderBox
)
pivot
=
pivot
.
parent
;
assert
(
pivot
.
parent
!=
null
);
assert
(
pivot
.
parent
!=
this
);
assert
(
pivot
!=
this
);
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
GrowthDirection
growthDirection
=
pivotParent
.
constraints
.
growthDirection
;
switch
(
applyGrowthDirectionToAxisDirection
(
axisDirection
,
growthDirection
))
{
case
AxisDirection
.
up
:
leadingScrollOffset
=
pivot
.
size
.
height
-
bounds
.
bottom
;
targetMainAxisExtent
=
bounds
.
height
;
break
;
case
AxisDirection
.
right
:
leadingScrollOffset
=
bounds
.
left
;
targetMainAxisExtent
=
bounds
.
width
;
break
;
case
AxisDirection
.
down
:
leadingScrollOffset
=
bounds
.
top
;
targetMainAxisExtent
=
bounds
.
height
;
break
;
case
AxisDirection
.
left
:
leadingScrollOffset
=
pivot
.
size
.
width
-
bounds
.
right
;
targetMainAxisExtent
=
bounds
.
width
;
break
;
}
descendant
=
pivot
;
}
else
if
(
target
is
RenderSliver
)
{
final
RenderSliver
targetSliver
=
target
;
leadingScrollOffset
=
0.0
;
targetMainAxisExtent
=
targetSliver
.
geometry
.
scrollExtent
;
descendant
=
targetSliver
;
}
else
{
return
offset
.
pixels
;
}
// The child will be the topmost object before we get to the viewport.
RenderObject
child
=
descendant
;
while
(
child
.
parent
is
RenderSliver
)
{
final
RenderSliver
parent
=
child
.
parent
;
leadingScrollOffset
+=
parent
.
childScrollOffset
(
child
);
child
=
parent
;
}
assert
(
child
.
parent
==
this
);
assert
(
child
is
RenderSliver
);
final
RenderSliver
sliver
=
child
;
leadingScrollOffset
=
scrollOffsetOf
(
sliver
,
leadingScrollOffset
);
double
mainAxisExtent
;
switch
(
axis
)
{
case
Axis
.
horizontal
:
mainAxisExtent
=
size
.
width
;
break
;
case
Axis
.
vertical
:
mainAxisExtent
=
size
.
height
;
break
;
}
return
leadingScrollOffset
-
(
mainAxisExtent
-
targetMainAxisExtent
)
*
alignment
;
}
@protected
Offset
computeAbsolutePaintOffset
(
RenderSliver
child
,
double
layoutOffset
,
GrowthDirection
growthDirection
)
{
assert
(
hasSize
);
// this is only usable once we have a size
assert
(
axisDirection
!=
null
);
assert
(
growthDirection
!=
null
);
assert
(
child
!=
null
);
assert
(
child
.
geometry
!=
null
);
switch
(
applyGrowthDirectionToAxisDirection
(
axisDirection
,
growthDirection
))
{
case
AxisDirection
.
up
:
return
new
Offset
(
0.0
,
size
.
height
-
(
layoutOffset
+
child
.
geometry
.
paintExtent
));
case
AxisDirection
.
right
:
return
new
Offset
(
layoutOffset
,
0.0
);
case
AxisDirection
.
down
:
return
new
Offset
(
0.0
,
layoutOffset
);
case
AxisDirection
.
left
:
return
new
Offset
(
size
.
width
-
(
layoutOffset
+
child
.
geometry
.
paintExtent
),
0.0
);
}
return
null
;
}
// TODO(ianh): semantics - shouldn't walk the invisible children
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'
$axisDirection
'
);
description
.
add
(
'offset:
$offset
'
);
}
@override
String
debugDescribeChildren
(
String
prefix
)
{
if
(
firstChild
==
null
)
return
'
$prefix
\n
'
;
int
count
=
indexOfFirstChild
();
String
result
=
'
$prefix
\
u2502
\n
'
;
RenderSliver
child
=
firstChild
;
while
(
child
!=
lastChild
)
{
result
+=
'
${child.toStringDeep("$prefix \u251C\u2500${labelForChild(count)}
: ", "
$prefix
\
u2502")}'
;
count
+=
1
;
child
=
childAfter
(
child
);
}
assert
(
child
==
lastChild
);
result
+=
'
${child.toStringDeep("$prefix \u2514\u2500${labelForChild(count)}
: ", "
$prefix
")}'
;
return
result
;
}
// API TO BE IMPLEMENTED BY SUBCLASSES
// setupParentData
// performLayout (and optionally sizedByParent and performResize)
@protected
bool
get
hasVisualOverflow
;
@protected
void
updateOutOfBoundsData
(
GrowthDirection
growthDirection
,
SliverGeometry
childLayoutGeometry
);
@protected
void
updateChildLayoutOffset
(
RenderSliver
child
,
double
layoutOffset
,
GrowthDirection
growthDirection
);
@protected
Offset
paintOffsetOf
(
RenderSliver
child
);
@protected
double
scrollOffsetOf
(
RenderSliver
child
,
double
scrollOffset
);
// applyPaintTransform
/// Converts the `parentMainAxisPosition` into the child's coordinate system.
///
/// The `parentMainAxisPosition` is a distance from the top edge (for vertical
/// viewports) or left edge (for horizontal viewports) of the viewport bounds.
/// This describes a line, perpendicular to the viewport's main axis, heretofor
/// known as the target line.
///
/// The child's coordinate system's origin in the main axis is at the leading
/// edge of the given child, as given by the child's
/// [SliverConstraints.axisDirection] and [SliverConstraints.growthDirection].
///
/// This method returns the distance from the leading edge of the given child to
/// the target line described above.
///
/// (The `parentMainAxisPosition` is not from the leading edge of the
/// viewport, it's always the top or left edge.)
@protected
double
computeChildMainAxisPosition
(
RenderSliver
child
,
double
parentMainAxisPosition
);
@protected
int
indexOfFirstChild
();
@protected
String
labelForChild
(
int
index
);
/// Provides an iterable that walks the children of the viewport, in the order
/// that they should be painted.
///
/// This should be the reverse order of [childrenInHitTestOrder].
@protected
Iterable
<
RenderSliver
>
get
childrenInPaintOrder
;
/// Provides an iterable that walks the children of the viewport, in the order
/// that hit-testing should use.
///
/// This should be the reverse order of [childrenInPaintOrder].
@protected
Iterable
<
RenderSliver
>
get
childrenInHitTestOrder
;
}
// ///
// /// See also:
// ///
// /// - [RenderSliver], which explains more about the Sliver protocol.
// /// - [RenderBox], which explains more about the Box protocol.
// /// - [RenderSliverToBoxAdapter], which allows a [RenderBox] object to be
// /// placed inside a [RenderSliver] (the opposite of this class).
// /// - [RenderShrinkWrappingViewport], a variant of [RenderViewport] that
// /// shrink-wraps its contents along the main axis.
class
RenderViewport
extends
RenderViewportBase
<
SliverPhysicalContainerParentData
>
{
/// Creates a viewport for [RenderSliver] objects.
///
/// If the [center] is not specified, then the first child in the `children`
/// list, if any, is used.
///
/// The [offset] must be specified. For testing purposes, consider passing a
/// [new ViewportOffset.zero] or [new ViewportOffset.fixed].
RenderViewport
({
AxisDirection
axisDirection:
AxisDirection
.
down
,
@required
ViewportOffset
offset
,
double
anchor:
0.0
,
List
<
RenderSliver
>
children
,
RenderSliver
center
,
})
:
_anchor
=
anchor
,
_center
=
center
,
super
(
axisDirection:
axisDirection
,
offset:
offset
)
{
assert
(
anchor
!=
null
);
assert
(
anchor
>=
0.0
&&
anchor
<=
1.0
);
addAll
(
children
);
if
(
center
==
null
&&
firstChild
!=
null
)
_center
=
firstChild
;
}
@override
void
setupParentData
(
RenderObject
child
)
{
if
(
child
.
parentData
is
!
SliverPhysicalContainerParentData
)
child
.
parentData
=
new
SliverPhysicalContainerParentData
();
}
double
get
anchor
=>
_anchor
;
double
_anchor
;
set
anchor
(
double
value
)
{
assert
(
value
!=
null
);
assert
(
value
>=
0.0
&&
value
<=
1.0
);
if
(
value
==
_anchor
)
return
;
_anchor
=
value
;
markNeedsLayout
();
}
RenderSliver
get
center
=>
_center
;
RenderSliver
_center
;
set
center
(
RenderSliver
value
)
{
if
(
value
==
_center
)
return
;
_center
=
value
;
markNeedsLayout
();
}
@override
bool
get
sizedByParent
=>
true
;
@override
void
performResize
()
{
assert
(
constraints
.
hasBoundedHeight
&&
constraints
.
hasBoundedWidth
);
size
=
constraints
.
biggest
;
// We ignore the return value of applyViewportDimension below because we are
// going to go through performLayout next regardless.
switch
(
axis
)
{
case
Axis
.
vertical
:
offset
.
applyViewportDimension
(
size
.
height
);
break
;
case
Axis
.
horizontal
:
offset
.
applyViewportDimension
(
size
.
width
);
break
;
}
}
static
const
int
_kMaxLayoutCycles
=
10
;
// Out-of-band data computed during layout.
double
_minScrollExtent
;
double
_maxScrollExtent
;
bool
_hasVisualOverflow
=
false
;
@override
void
performLayout
()
{
if
(
center
==
null
)
{
assert
(
firstChild
==
null
);
_minScrollExtent
=
0.0
;
_maxScrollExtent
=
0.0
;
_hasVisualOverflow
=
false
;
offset
.
applyContentDimensions
(
0.0
,
0.0
);
return
;
}
assert
(
center
.
parent
==
this
);
double
mainAxisExtent
;
double
crossAxisExtent
;
switch
(
axis
)
{
case
Axis
.
vertical
:
mainAxisExtent
=
size
.
height
;
crossAxisExtent
=
size
.
width
;
break
;
case
Axis
.
horizontal
:
mainAxisExtent
=
size
.
width
;
crossAxisExtent
=
size
.
height
;
break
;
}
final
double
centerOffsetAdjustment
=
center
.
centerOffsetAdjustment
;
double
correction
;
int
count
=
0
;
do
{
assert
(
offset
.
pixels
!=
null
);
correction
=
_attemptLayout
(
mainAxisExtent
,
crossAxisExtent
,
offset
.
pixels
+
centerOffsetAdjustment
);
if
(
correction
!=
0.0
)
{
offset
.
correctBy
(
correction
);
}
else
{
if
(
offset
.
applyContentDimensions
(
math
.
min
(
0.0
,
_minScrollExtent
+
mainAxisExtent
*
anchor
),
math
.
max
(
0.0
,
_maxScrollExtent
-
mainAxisExtent
*
(
1.0
-
anchor
)),
))
break
;
}
count
+=
1
;
}
while
(
count
<
_kMaxLayoutCycles
);
assert
(()
{
if
(
count
>=
_kMaxLayoutCycles
)
{
assert
(
count
!=
1
);
throw
new
FlutterError
(
'A RenderViewport exceeded its maximum number of layout cycles.
\n
'
'RenderViewport render objects, during layout, can retry if either their '
'slivers or their ViewportOffset decide that the offset should be corrected '
'to take into account information collected during that layout.
\n
'
'In the case of this RenderViewport object, however, this happened
$count
'
'times and still there was no consensus on the scroll offset. This usually '
'indicates a bug. Specifically, it means that one of the following three '
'problems is being experienced by the RenderViewport object:
\n
'
' * One of the RenderSliver children or the ViewportOffset have a bug such'
' that they always think that they need to correct the offset regardless.
\n
'
' * Some combination of the RenderSliver children and the ViewportOffset'
' have a bad interaction such that one applies a correction then another'
' applies a reverse correction, leading to an infinite loop of corrections.
\n
'
' * There is a pathological case that would eventually resolve, but it is'
' so complicated that it cannot be resolved in any reasonable number of'
' layout passes.'
);
}
return
true
;
});
}
double
_attemptLayout
(
double
mainAxisExtent
,
double
crossAxisExtent
,
double
correctedOffset
)
{
assert
(!
mainAxisExtent
.
isNaN
);
assert
(
mainAxisExtent
>=
0.0
);
assert
(
crossAxisExtent
.
isFinite
);
assert
(
crossAxisExtent
>=
0.0
);
assert
(
correctedOffset
.
isFinite
);
_minScrollExtent
=
0.0
;
_maxScrollExtent
=
0.0
;
_hasVisualOverflow
=
false
;
// centerOffset is the offset from the leading edge of the RenderViewport
// to the zero scroll offset (the line between the forward slivers and the
// reverse slivers). The other two are that, but clamped to the visible
// region of the viewport.
final
double
centerOffset
=
mainAxisExtent
*
anchor
-
correctedOffset
;
final
double
clampedForwardCenter
=
math
.
max
(
0.0
,
math
.
min
(
mainAxisExtent
,
centerOffset
));
final
double
clampedReverseCenter
=
math
.
max
(
0.0
,
math
.
min
(
mainAxisExtent
,
mainAxisExtent
-
centerOffset
));
// negative scroll offsets
double
result
=
layoutOneSide
(
childBefore
(
center
),
math
.
max
(
mainAxisExtent
,
mainAxisExtent
*
anchor
-
correctedOffset
)
-
mainAxisExtent
,
clampedReverseCenter
,
clampedForwardCenter
,
mainAxisExtent
,
crossAxisExtent
,
GrowthDirection
.
reverse
,
childBefore
,
);
if
(
result
!=
0.0
)
return
-
result
;
// positive scroll offsets
return
layoutOneSide
(
center
,
math
.
max
(
0.0
,
correctedOffset
-
mainAxisExtent
*
anchor
),
clampedForwardCenter
,
clampedReverseCenter
,
mainAxisExtent
,
crossAxisExtent
,
GrowthDirection
.
forward
,
childAfter
,
);
}
@override
bool
get
hasVisualOverflow
=>
_hasVisualOverflow
;
@override
void
updateOutOfBoundsData
(
GrowthDirection
growthDirection
,
SliverGeometry
childLayoutGeometry
)
{
switch
(
growthDirection
)
{
case
GrowthDirection
.
forward
:
_maxScrollExtent
+=
childLayoutGeometry
.
scrollExtent
;
break
;
case
GrowthDirection
.
reverse
:
_minScrollExtent
-=
childLayoutGeometry
.
scrollExtent
;
break
;
}
if
(
childLayoutGeometry
.
hasVisualOverflow
)
_hasVisualOverflow
=
true
;
}
@override
void
updateChildLayoutOffset
(
RenderSliver
child
,
double
layoutOffset
,
GrowthDirection
growthDirection
)
{
final
SliverPhysicalParentData
childParentData
=
child
.
parentData
;
childParentData
.
paintOffset
=
computeAbsolutePaintOffset
(
child
,
layoutOffset
,
growthDirection
);
}
@override
Offset
paintOffsetOf
(
RenderSliver
child
)
{
final
SliverPhysicalParentData
childParentData
=
child
.
parentData
;
return
childParentData
.
paintOffset
;
}
@override
double
scrollOffsetOf
(
RenderSliver
child
,
double
scrollOffsetWithinChild
)
{
assert
(
child
.
parent
==
this
);
final
GrowthDirection
growthDirection
=
child
.
constraints
.
growthDirection
;
assert
(
growthDirection
!=
null
);
switch
(
growthDirection
)
{
case
GrowthDirection
.
forward
:
double
scrollOffsetToChild
=
0.0
;
RenderSliver
current
=
center
;
while
(
current
!=
child
)
{
scrollOffsetToChild
+=
current
.
geometry
.
scrollExtent
;
current
=
childAfter
(
current
);
}
return
scrollOffsetToChild
+
scrollOffsetWithinChild
;
case
GrowthDirection
.
reverse
:
double
scrollOffsetToChild
=
0.0
;
RenderSliver
current
=
childBefore
(
center
);
while
(
current
!=
child
)
{
scrollOffsetToChild
-=
current
.
geometry
.
scrollExtent
;
current
=
childBefore
(
current
);
}
return
scrollOffsetToChild
-
scrollOffsetWithinChild
;
}
return
null
;
}
@override
void
applyPaintTransform
(
RenderObject
child
,
Matrix4
transform
)
{
assert
(
child
!=
null
);
final
SliverPhysicalParentData
childParentData
=
child
.
parentData
;
childParentData
.
applyPaintTransform
(
transform
);
}
@override
double
computeChildMainAxisPosition
(
RenderSliver
child
,
double
parentMainAxisPosition
)
{
assert
(
child
!=
null
);
assert
(
child
.
constraints
!=
null
);
final
SliverPhysicalParentData
childParentData
=
child
.
parentData
;
switch
(
applyGrowthDirectionToAxisDirection
(
child
.
constraints
.
axisDirection
,
child
.
constraints
.
growthDirection
))
{
case
AxisDirection
.
down
:
return
parentMainAxisPosition
-
childParentData
.
paintOffset
.
dy
;
case
AxisDirection
.
right
:
return
parentMainAxisPosition
-
childParentData
.
paintOffset
.
dx
;
case
AxisDirection
.
up
:
return
child
.
geometry
.
paintExtent
-
(
parentMainAxisPosition
-
childParentData
.
paintOffset
.
dy
);
case
AxisDirection
.
left
:
return
child
.
geometry
.
paintExtent
-
(
parentMainAxisPosition
-
childParentData
.
paintOffset
.
dx
);
}
return
0.0
;
}
@override
int
indexOfFirstChild
()
{
assert
(
center
!=
null
);
assert
(
center
.
parent
==
this
);
assert
(
firstChild
!=
null
);
int
count
=
0
;
RenderSliver
child
=
center
;
while
(
child
!=
firstChild
)
{
count
-=
1
;
child
=
childBefore
(
child
);
}
return
count
;
}
@override
String
labelForChild
(
int
index
)
{
if
(
index
==
0
)
return
'center child'
;
return
'child
$index
'
;
}
@override
Iterable
<
RenderSliver
>
get
childrenInPaintOrder
sync
*
{
if
(
firstChild
==
null
)
return
;
RenderSliver
child
=
firstChild
;
while
(
child
!=
center
)
{
yield
child
;
child
=
childAfter
(
child
);
}
child
=
lastChild
;
while
(
true
)
{
yield
child
;
if
(
child
==
center
)
return
;
child
=
childBefore
(
child
);
}
}
@override
Iterable
<
RenderSliver
>
get
childrenInHitTestOrder
sync
*
{
if
(
firstChild
==
null
)
return
;
RenderSliver
child
=
center
;
while
(
child
!=
null
)
{
yield
child
;
child
=
childAfter
(
child
);
}
child
=
childBefore
(
center
);
while
(
child
!=
null
)
{
yield
child
;
child
=
childBefore
(
child
);
}
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'anchor:
$anchor
'
);
}
}
// ///
// /// See also:
// ///
// /// - [RenderViewport], a viewport that does not shrink-wrap its contents
// /// - [RenderSliver], which explains more about the Sliver protocol.
// /// - [RenderBox], which explains more about the Box protocol.
// /// - [RenderSliverToBoxAdapter], which allows a [RenderBox] object to be
// /// placed inside a [RenderSliver] (the opposite of this class).
class
RenderShrinkWrappingViewport
extends
RenderViewportBase
<
SliverLogicalContainerParentData
>
{
/// Creates a viewport (for [RenderSliver] objects) that shrink-wraps its
/// contents.
///
/// The [offset] must be specified. For testing purposes, consider passing a
/// [new ViewportOffset.zero] or [new ViewportOffset.fixed].
RenderShrinkWrappingViewport
({
AxisDirection
axisDirection:
AxisDirection
.
down
,
@required
ViewportOffset
offset
,
List
<
RenderSliver
>
children
,
})
:
super
(
axisDirection:
axisDirection
,
offset:
offset
)
{
addAll
(
children
);
}
@override
void
setupParentData
(
RenderObject
child
)
{
if
(
child
.
parentData
is
!
SliverLogicalContainerParentData
)
child
.
parentData
=
new
SliverLogicalContainerParentData
();
}
// Out-of-band data computed during layout.
double
_maxScrollExtent
;
double
_shrinkWrapExtent
;
bool
_hasVisualOverflow
=
false
;
@override
void
performLayout
()
{
if
(
firstChild
==
null
)
{
switch
(
axis
)
{
case
Axis
.
vertical
:
assert
(
constraints
.
hasBoundedWidth
);
size
=
new
Size
(
constraints
.
maxWidth
,
constraints
.
minHeight
);
break
;
case
Axis
.
horizontal
:
assert
(
constraints
.
hasBoundedHeight
);
size
=
new
Size
(
constraints
.
minWidth
,
constraints
.
maxHeight
);
break
;
}
offset
.
applyViewportDimension
(
0.0
);
_maxScrollExtent
=
0.0
;
_shrinkWrapExtent
=
0.0
;
_hasVisualOverflow
=
false
;
offset
.
applyContentDimensions
(
0.0
,
0.0
);
return
;
}
double
mainAxisExtent
;
double
crossAxisExtent
;
switch
(
axis
)
{
case
Axis
.
vertical
:
assert
(
constraints
.
hasBoundedWidth
);
mainAxisExtent
=
constraints
.
maxHeight
;
crossAxisExtent
=
constraints
.
maxWidth
;
break
;
case
Axis
.
horizontal
:
assert
(
constraints
.
hasBoundedHeight
);
mainAxisExtent
=
constraints
.
maxWidth
;
crossAxisExtent
=
constraints
.
maxHeight
;
break
;
}
double
correction
;
double
effectiveExtent
;
do
{
assert
(
offset
.
pixels
!=
null
);
correction
=
_attemptLayout
(
mainAxisExtent
,
crossAxisExtent
,
offset
.
pixels
);
if
(
correction
!=
0.0
)
{
offset
.
correctBy
(
correction
);
}
else
{
switch
(
axis
)
{
case
Axis
.
vertical
:
effectiveExtent
=
constraints
.
constrainHeight
(
_shrinkWrapExtent
);
break
;
case
Axis
.
horizontal
:
effectiveExtent
=
constraints
.
constrainWidth
(
_shrinkWrapExtent
);
break
;
}
final
bool
didAcceptViewportDimension
=
offset
.
applyViewportDimension
(
effectiveExtent
);
final
bool
didAcceptContentDimension
=
offset
.
applyContentDimensions
(
0.0
,
math
.
max
(
0.0
,
_maxScrollExtent
-
effectiveExtent
));
if
(
didAcceptViewportDimension
&&
didAcceptContentDimension
)
break
;
}
}
while
(
true
);
switch
(
axis
)
{
case
Axis
.
vertical
:
size
=
constraints
.
constrainDimensions
(
crossAxisExtent
,
effectiveExtent
);
break
;
case
Axis
.
horizontal
:
size
=
constraints
.
constrainDimensions
(
effectiveExtent
,
crossAxisExtent
);
break
;
}
}
double
_attemptLayout
(
double
mainAxisExtent
,
double
crossAxisExtent
,
double
correctedOffset
)
{
assert
(!
mainAxisExtent
.
isNaN
);
assert
(
mainAxisExtent
>=
0.0
);
assert
(
crossAxisExtent
.
isFinite
);
assert
(
crossAxisExtent
>=
0.0
);
assert
(
correctedOffset
.
isFinite
);
_maxScrollExtent
=
0.0
;
_shrinkWrapExtent
=
0.0
;
_hasVisualOverflow
=
false
;
return
layoutOneSide
(
firstChild
,
math
.
max
(
0.0
,
correctedOffset
),
0.0
,
mainAxisExtent
,
mainAxisExtent
,
crossAxisExtent
,
GrowthDirection
.
forward
,
childAfter
,
);
}
@override
bool
get
hasVisualOverflow
=>
_hasVisualOverflow
;
@override
void
updateOutOfBoundsData
(
GrowthDirection
growthDirection
,
SliverGeometry
childLayoutGeometry
)
{
assert
(
growthDirection
==
GrowthDirection
.
forward
);
_maxScrollExtent
+=
childLayoutGeometry
.
scrollExtent
;
if
(
childLayoutGeometry
.
hasVisualOverflow
)
_hasVisualOverflow
=
true
;
_shrinkWrapExtent
+=
childLayoutGeometry
.
maxPaintExtent
;
}
@override
void
updateChildLayoutOffset
(
RenderSliver
child
,
double
layoutOffset
,
GrowthDirection
growthDirection
)
{
assert
(
growthDirection
==
GrowthDirection
.
forward
);
final
SliverLogicalParentData
childParentData
=
child
.
parentData
;
childParentData
.
layoutOffset
=
layoutOffset
;
}
@override
Offset
paintOffsetOf
(
RenderSliver
child
)
{
final
SliverLogicalParentData
childParentData
=
child
.
parentData
;
return
computeAbsolutePaintOffset
(
child
,
childParentData
.
layoutOffset
,
GrowthDirection
.
forward
);
}
@override
double
scrollOffsetOf
(
RenderSliver
child
,
double
scrollOffsetWithinChild
)
{
assert
(
child
.
parent
==
this
);
assert
(
child
.
constraints
.
growthDirection
==
GrowthDirection
.
forward
);
double
scrollOffsetToChild
=
0.0
;
RenderSliver
current
=
firstChild
;
while
(
current
!=
child
)
{
scrollOffsetToChild
+=
current
.
geometry
.
scrollExtent
;
current
=
childAfter
(
current
);
}
return
scrollOffsetToChild
+
scrollOffsetWithinChild
;
}
@override
void
applyPaintTransform
(
RenderObject
child
,
Matrix4
transform
)
{
assert
(
child
!=
null
);
final
Offset
offset
=
paintOffsetOf
(
child
);
transform
.
translate
(
offset
.
dx
,
offset
.
dy
);
}
@override
double
computeChildMainAxisPosition
(
RenderSliver
child
,
double
parentMainAxisPosition
)
{
assert
(
child
!=
null
);
assert
(
child
.
constraints
!=
null
);
assert
(
hasSize
);
final
SliverLogicalParentData
childParentData
=
child
.
parentData
;
switch
(
applyGrowthDirectionToAxisDirection
(
child
.
constraints
.
axisDirection
,
child
.
constraints
.
growthDirection
))
{
case
AxisDirection
.
down
:
case
AxisDirection
.
right
:
return
parentMainAxisPosition
-
childParentData
.
layoutOffset
;
case
AxisDirection
.
up
:
return
(
size
.
height
-
parentMainAxisPosition
)
-
childParentData
.
layoutOffset
;
case
AxisDirection
.
left
:
return
(
size
.
width
-
parentMainAxisPosition
)
-
childParentData
.
layoutOffset
;
}
return
0.0
;
}
@override
int
indexOfFirstChild
()
=>
0
;
@override
String
labelForChild
(
int
index
)
=>
'child
$index
'
;
@override
Iterable
<
RenderSliver
>
get
childrenInPaintOrder
sync
*
{
RenderSliver
child
=
firstChild
;
while
(
child
!=
null
)
{
yield
child
;
child
=
childAfter
(
child
);
}
}
@override
Iterable
<
RenderSliver
>
get
childrenInHitTestOrder
sync
*
{
RenderSliver
child
=
lastChild
;
while
(
child
!=
null
)
{
yield
child
;
child
=
childBefore
(
child
);
}
}
}
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