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
8ce2f859
Commit
8ce2f859
authored
Apr 24, 2016
by
Adam Barth
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #3516 from abarth/tweaks
Add Flow layout
parents
9d900ea7
f53a5a52
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
643 additions
and
24 deletions
+643
-24
rendering.dart
packages/flutter/lib/rendering.dart
+1
-0
snack_bar.dart
packages/flutter/lib/src/material/snack_bar.dart
+1
-1
box.dart
packages/flutter/lib/src/rendering/box.dart
+14
-3
flex.dart
packages/flutter/lib/src/rendering/flex.dart
+1
-1
flow.dart
packages/flutter/lib/src/rendering/flow.dart
+388
-0
proxy_box.dart
packages/flutter/lib/src/rendering/proxy_box.dart
+10
-3
shifted_box.dart
packages/flutter/lib/src/rendering/shifted_box.dart
+6
-6
stack.dart
packages/flutter/lib/src/rendering/stack.dart
+5
-1
image_resource.dart
packages/flutter/lib/src/services/image_resource.dart
+1
-1
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+126
-8
flow_test.dart
packages/flutter/test/widget/flow_test.dart
+90
-0
No files found.
packages/flutter/lib/rendering.dart
View file @
8ce2f859
...
...
@@ -30,6 +30,7 @@ export 'src/rendering/debug.dart';
export
'src/rendering/editable_line.dart'
;
export
'src/rendering/error.dart'
;
export
'src/rendering/flex.dart'
;
export
'src/rendering/flow.dart'
;
export
'src/rendering/grid.dart'
;
export
'src/rendering/image.dart'
;
export
'src/rendering/layer.dart'
;
...
...
packages/flutter/lib/src/material/snack_bar.dart
View file @
8ce2f859
...
...
@@ -51,7 +51,7 @@ class SnackBarAction extends StatefulWidget {
/// The button label.
final
String
label
;
/// The callback to be invoked when the button is pressed. Must
be non-
null.
/// The callback to be invoked when the button is pressed. Must
not be
null.
///
/// This callback will be invoked at most once each time this action is
/// displayed in a [SnackBar].
...
...
packages/flutter/lib/src/rendering/box.dart
View file @
8ce2f859
...
...
@@ -1049,7 +1049,7 @@ abstract class RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, Pare
/// appear in the child list.
double
defaultComputeDistanceToFirstActualBaseline
(
TextBaseline
baseline
)
{
assert
(!
needsLayout
);
RenderBox
child
=
firstChild
;
ChildType
child
=
firstChild
;
while
(
child
!=
null
)
{
final
ParentDataType
childParentData
=
child
.
parentData
;
double
result
=
child
.
getDistanceToActualBaseline
(
baseline
);
...
...
@@ -1067,7 +1067,7 @@ abstract class RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, Pare
double
defaultComputeDistanceToHighestActualBaseline
(
TextBaseline
baseline
)
{
assert
(!
needsLayout
);
double
result
;
RenderBox
child
=
firstChild
;
ChildType
child
=
firstChild
;
while
(
child
!=
null
)
{
final
ParentDataType
childParentData
=
child
.
parentData
;
double
candidate
=
child
.
getDistanceToActualBaseline
(
baseline
);
...
...
@@ -1103,13 +1103,24 @@ abstract class RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, Pare
/// Paints each child by walking the child list forwards.
void
defaultPaint
(
PaintingContext
context
,
Offset
offset
)
{
RenderBox
child
=
firstChild
;
ChildType
child
=
firstChild
;
while
(
child
!=
null
)
{
final
ParentDataType
childParentData
=
child
.
parentData
;
context
.
paintChild
(
child
,
childParentData
.
offset
+
offset
);
child
=
childParentData
.
nextSibling
;
}
}
List
<
ChildType
>
getChildrenAsList
()
{
List
<
ChildType
>
result
=
<
ChildType
>[];
RenderBox
child
=
firstChild
;
while
(
child
!=
null
)
{
final
ParentDataType
childParentData
=
child
.
parentData
;
result
.
add
(
child
);
child
=
childParentData
.
nextSibling
;
}
return
result
;
}
}
class
FractionalOffsetTween
extends
Tween
<
FractionalOffset
>
{
...
...
packages/flutter/lib/src/rendering/flex.dart
View file @
8ce2f859
...
...
@@ -7,7 +7,7 @@ import 'dart:math' as math;
import
'box.dart'
;
import
'object.dart'
;
/// Parent data for use with [RenderFlex]
/// Parent data for use with [RenderFlex]
.
class
FlexParentData
extends
ContainerBoxParentDataMixin
<
RenderBox
>
{
/// The flex factor to use for this child
///
...
...
packages/flutter/lib/src/rendering/flow.dart
0 → 100644
View file @
8ce2f859
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/animation.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
import
'box.dart'
;
import
'object.dart'
;
/// A context in which a [FlowDelegate] paints.
///
/// Provides information about the current size of the container and the
/// children and a mechanism for painting children.
///
/// See also:
///
/// * [FlowDelegate]
/// * [Flow]
/// * [RenderFlow]
abstract
class
FlowPaintingContext
{
/// The size of the container in which the children can be painted.
Size
get
size
;
/// The number of children available to paint.
int
get
childCount
;
/// The size of the [i]th child.
///
/// If [i] is negative or exceeds [childCount], returns null.
Size
getChildSize
(
int
i
);
/// Paint the [i]th child using the given transform.
///
/// The child will be painted in a coordinate system that concatenates the
/// container's coordinate system with the given transform. The origin of the
/// parent's coordinate system is the upper left corner of the parent, with
/// x increasing rightward and y increasing downward.
///
/// The container will clip the children to its bounds.
void
paintChild
(
int
i
,
{
Matrix4
transform
,
double
opacity:
1.0
});
}
/// A delegate that controls the appearance of a flow layout.
///
/// Flow layouts are optimized for moving children around the screen using
/// transformation matrices. For optimal performance, construct the
/// [FlowDelegate] with an [Animation] that ticks whenever the delegate wishes
/// to change the transformation matrices for the children and avoid rebuilding
/// the [Flow] widget itself every animation frame.
///
/// See also:
///
/// * [Flow]
/// * [RenderFlow]
abstract
class
FlowDelegate
{
/// The flow will repaint whenever the [repaint] animation ticks.
const
FlowDelegate
({
Animation
<
dynamic
>
repaint
})
:
_repaint
=
repaint
;
final
Animation
<
dynamic
>
_repaint
;
/// Override to control the size of the container for the children.
///
/// By default, the flow will be as large as possible. If this function
/// returns a size that does not respect the given constraints, the size will
/// be adjusted to be as close to the returned size as possible while still
/// respecting the constraints.
///
/// If this function depends on information other than the given constraints,
/// override [shouldRelayout] to indicate when when the container should
/// relayout.
Size
getSize
(
BoxConstraints
constraints
)
=>
constraints
.
biggest
;
/// Override to control the layout constraints given to each child.
///
/// By default, the children will receive the given constraints, which are the
/// constrains the constraints used to size the container. The children need
/// not respect the given constraints, but they are required to respect the
/// returned constraints. For example, the incoming constraings might require
/// the container to have a width of exactly 100.0 and a height of exactly
/// 100.0, but this function might give the children looser constraints that
/// let them be larger or smaller than 100.0 by 100.0.
///
/// If this function depends on information other than the given constraints,
/// override [shouldRelayout] to indicate when when the container should
/// relayout.
BoxConstraints
getConstraintsForChild
(
int
i
,
BoxConstraints
constraints
)
=>
constraints
;
/// Override to paint the children of the flow.
///
/// Children can be painted in any order, but each child can be painted at
/// most once. Although the container clips the children to its own bounds, it
/// is more efficient to skip painting a child altogether rather than having
/// it paint entirely outside the container's clip.
///
/// To paint a child, call [FlowPaintingContext.paintChild] on the given
/// [context]. The given context is valid only within the scope of this
/// function call and contains information (such as the size of the container)
/// that is useful for picking transformation matrices for the children.
///
/// If this function depends on information other than the given context,
/// override [shouldRepaint] to indicate when when the container should
/// relayout.
void
paintChildren
(
FlowPaintingContext
context
);
/// Override this method to return true when the children need to be laid out.
/// This should compare the fields of the current delegate and the given
/// oldDelegate and return true if the fields are such that the layout would
/// be different.
bool
shouldRelayout
(
FlowDelegate
oldDelegate
)
=>
false
;
/// Override this method to return true when the children need to be
/// repainted. This should compare the fields of the current delegate and the
/// given oldDelegate and return true if the fields are such that
/// paintChildren would act differently.
///
/// The delegate can also trigger a repaint if the delegate provides the
/// repaint animation argument to this object's constructor and that animation
/// ticks. Triggering a repaint using this animation-based mechanism is more
/// efficient than rebuilding the [Flow] widget to change its delegate.
///
/// The flow container might repaint even if this function returns false, for
/// example if layout triggers painting (e.g., if [shouldRelayout] returns
/// true).
bool
shouldRepaint
(
FlowDelegate
oldDelegate
);
/// Override this method to include additional information in the
/// debugging data printed by [debugDumpRenderTree] and friends.
///
/// By default, returns the [runtimeType] of the class.
@override
String
toString
()
=>
'
$runtimeType
'
;
}
int
_getAlphaFromOpacity
(
double
opacity
)
=>
(
opacity
*
255
).
round
();
/// Parent data for use with [RenderFlow].
///
/// The [offset] property is ignored by [RenderFlow] and is always set to
/// [Offset.zero]. Children of a [RenderFlow] are positioned using a
/// transformation matrix, which is private to the [RenderFlow]. To set the
/// matrix, use the [FlowPaintingContext.paintChild] function from an override
/// of the [FlowDelegate.paintChildren] function.
class
FlowParentData
extends
ContainerBoxParentDataMixin
<
RenderBox
>
{
Matrix4
_transform
;
}
/// Implements the flow layout algorithm.
///
/// Flow layouts are optimized for repositioning children using transformation
/// matrices.
///
/// The flow container is sized independently from the children by the
/// [FlowDelegate.getSize] function of the delegate. The children are then sized
/// independently given the constraints from the
/// [FlowDelegate.getConstraintsForChild] function.
///
/// Rather than positioning the children during layout, the children are
/// positioned using transformation matrices during the paint phase using the
/// matrices from the [FlowDelegate.paintChildren] function. The children can be
/// repositioned efficiently by simply repainting the flow.
///
/// The most efficient way to trigger a repaint of the flow is to supply a
/// repaint argument to the constructor of the [FlowDelegate]. The flow will
/// listen to this animation and repaint whenever the animation ticks, avoiding
/// both the build and layout phases of the pipeline.
///
/// See also:
///
/// * [FlowDelegate]
/// * [RenderStack]
class
RenderFlow
extends
RenderBox
with
ContainerRenderObjectMixin
<
RenderBox
,
FlowParentData
>,
RenderBoxContainerDefaultsMixin
<
RenderBox
,
FlowParentData
>
implements
FlowPaintingContext
{
/// Creates a render object for a flow layout.
///
/// For optimal performance, consider using children that return true from
/// [isRepaintBounday].
RenderFlow
({
List
<
RenderBox
>
children
,
FlowDelegate
delegate
})
:
_delegate
=
delegate
{
assert
(
delegate
!=
null
);
addAll
(
children
);
}
@override
void
setupParentData
(
RenderBox
child
)
{
final
ParentData
childParentData
=
child
.
parentData
;
if
(
childParentData
is
FlowParentData
)
childParentData
.
_transform
=
null
;
else
child
.
parentData
=
new
FlowParentData
();
}
/// The delegate that controls the transformation matrices of the children.
FlowDelegate
get
delegate
=>
_delegate
;
FlowDelegate
_delegate
;
/// When the delegate is changed to a new delegate with the same runtimeType
/// as the old delegate, this object will call the delegate's
/// [FlowDelegate.shouldRelayout] and [FlowDelegate.shouldRepaint] functions
/// to determine whether the new delegate requires this object to update its
/// layout or painting.
void
set
delegate
(
FlowDelegate
newDelegate
)
{
assert
(
newDelegate
!=
null
);
if
(
_delegate
==
newDelegate
)
return
;
final
FlowDelegate
oldDelegate
=
_delegate
;
_delegate
=
newDelegate
;
if
(
newDelegate
.
runtimeType
!=
oldDelegate
.
runtimeType
||
newDelegate
.
shouldRelayout
(
oldDelegate
))
markNeedsLayout
();
else
if
(
newDelegate
.
shouldRepaint
(
oldDelegate
))
markNeedsPaint
();
if
(
attached
)
{
oldDelegate
.
_repaint
?.
removeListener
(
markNeedsPaint
);
newDelegate
.
_repaint
?.
addListener
(
markNeedsPaint
);
}
}
@override
void
attach
(
PipelineOwner
owner
)
{
super
.
attach
(
owner
);
_delegate
.
_repaint
?.
addListener
(
markNeedsPaint
);
}
@override
void
detach
()
{
_delegate
.
_repaint
?.
removeListener
(
markNeedsPaint
);
super
.
detach
();
}
Size
_getSize
(
BoxConstraints
constraints
)
{
assert
(
constraints
.
debugAssertIsValid
());
return
constraints
.
constrain
(
_delegate
.
getSize
(
constraints
));
}
@override
bool
get
isRepaintBoundary
=>
true
;
@override
double
getMinIntrinsicWidth
(
BoxConstraints
constraints
)
{
return
_getSize
(
constraints
).
width
;
}
@override
double
getMaxIntrinsicWidth
(
BoxConstraints
constraints
)
{
return
_getSize
(
constraints
).
width
;
}
@override
double
getMinIntrinsicHeight
(
BoxConstraints
constraints
)
{
return
_getSize
(
constraints
).
height
;
}
@override
double
getMaxIntrinsicHeight
(
BoxConstraints
constraints
)
{
return
_getSize
(
constraints
).
height
;
}
@override
void
performLayout
()
{
size
=
_getSize
(
constraints
);
int
i
=
0
;
_randomAccessChildren
.
clear
();
RenderBox
child
=
firstChild
;
while
(
child
!=
null
)
{
_randomAccessChildren
.
add
(
child
);
BoxConstraints
innerConstraints
=
_delegate
.
getConstraintsForChild
(
i
,
constraints
);
child
.
layout
(
innerConstraints
,
parentUsesSize:
true
);
final
FlowParentData
childParentData
=
child
.
parentData
;
childParentData
.
offset
=
Offset
.
zero
;
child
=
childParentData
.
nextSibling
;
i
+=
1
;
}
}
// Updated during layout. Only valid if layout is not dirty.
final
List
<
RenderBox
>
_randomAccessChildren
=
<
RenderBox
>[];
// Updated during paint.
final
List
<
int
>
_lastPaintOrder
=
<
int
>[];
// Only valid during paint.
PaintingContext
_paintingContext
;
Offset
_paintingOffset
;
@override
Size
getChildSize
(
int
i
)
{
if
(
i
<
0
||
i
>=
_randomAccessChildren
.
length
)
return
null
;
return
_randomAccessChildren
[
i
].
size
;
}
@override
void
paintChild
(
int
i
,
{
Matrix4
transform
,
double
opacity:
1.0
})
{
transform
??=
new
Matrix4
.
identity
();
RenderBox
child
=
_randomAccessChildren
[
i
];
final
FlowParentData
childParentData
=
child
.
parentData
;
assert
(()
{
if
(
childParentData
.
_transform
!=
null
)
{
throw
new
FlutterError
(
'Cannot call paintChild twice for the same child.
\n
'
'The flow delegate of type
${_delegate.runtimeType}
attempted to '
'paint child
$i
multiple times, which is not permitted.'
);
}
return
true
;
});
_lastPaintOrder
.
add
(
i
);
childParentData
.
_transform
=
transform
;
// We return after assigning _transform so that the transparent child can
// still be hit tested at the correct location.
if
(
opacity
==
0.0
)
return
;
void
painter
(
PaintingContext
context
,
Offset
offset
)
{
context
.
paintChild
(
child
,
offset
);
};
if
(
opacity
==
1.0
)
{
_paintingContext
.
pushTransform
(
needsCompositing
,
_paintingOffset
,
transform
,
painter
);
}
else
{
_paintingContext
.
pushOpacity
(
_paintingOffset
,
_getAlphaFromOpacity
(
opacity
),
(
PaintingContext
context
,
Offset
offset
)
{
_paintingContext
.
pushTransform
(
needsCompositing
,
offset
,
transform
,
painter
);
});
}
}
void
_paintWithDelegate
(
PaintingContext
context
,
Offset
offset
)
{
_lastPaintOrder
.
clear
();
_paintingContext
=
context
;
_paintingOffset
=
offset
;
for
(
RenderBox
child
in
_randomAccessChildren
)
{
final
FlowParentData
childParentData
=
child
.
parentData
;
childParentData
.
_transform
=
null
;
}
try
{
_delegate
.
paintChildren
(
this
);
}
finally
{
_paintingContext
=
null
;
_paintingOffset
=
null
;
}
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
context
.
pushClipRect
(
needsCompositing
,
offset
,
Point
.
origin
&
size
,
_paintWithDelegate
);
}
@override
bool
hitTestChildren
(
HitTestResult
result
,
{
Point
position
})
{
final
List
<
RenderBox
>
children
=
getChildrenAsList
();
for
(
int
i
=
_lastPaintOrder
.
length
-
1
;
i
>=
0
;
--
i
)
{
final
int
childIndex
=
_lastPaintOrder
[
i
];
if
(
childIndex
>=
children
.
length
)
continue
;
final
RenderBox
child
=
children
[
childIndex
];
final
FlowParentData
childParentData
=
child
.
parentData
;
final
Matrix4
transform
=
childParentData
.
_transform
;
if
(
transform
==
null
)
continue
;
Matrix4
inverse
=
new
Matrix4
.
zero
();
double
determinate
=
inverse
.
copyInverse
(
transform
);
if
(
determinate
==
0.0
)
{
// We cannot invert the transform. That means the child doesn't appear
// on screen and cannot be hit.
continue
;
}
final
Vector3
position3
=
new
Vector3
(
position
.
x
,
position
.
y
,
0.0
);
final
Vector3
transformed3
=
inverse
.
transform3
(
position3
);
Point
childPosition
=
new
Point
(
transformed3
.
x
,
transformed3
.
y
);
if
(
child
.
hitTest
(
result
,
position:
childPosition
))
return
true
;
}
return
false
;
}
@override
void
applyPaintTransform
(
RenderBox
child
,
Matrix4
transform
)
{
final
FlowParentData
childParentData
=
child
.
parentData
;
if
(
childParentData
.
_transform
!=
null
)
transform
.
multiply
(
childParentData
.
_transform
);
super
.
applyPaintTransform
(
child
,
transform
);
}
}
packages/flutter/lib/src/rendering/proxy_box.dart
View file @
8ce2f859
...
...
@@ -806,6 +806,7 @@ abstract class CustomClipper<T> {
/// Returns a description of the clip given that the render object being
/// clipped is of the given size.
T
getClip
(
Size
size
);
/// Returns an approximation of the clip returned by [getClip], as
/// an axis-aligned Rect. This is used by the semantics layer to
/// determine whether widgets should be excluded.
...
...
@@ -815,6 +816,7 @@ abstract class CustomClipper<T> {
/// same size as the RenderObject (e.g. it's a rounded rectangle
/// with very small arcs in the corners), then this may be adequate.
Rect
getApproximateClipRect
(
Size
size
)
=>
Point
.
origin
&
size
;
/// Returns `true` if the new instance will result in a different clip
/// than the oldClipper instance.
bool
shouldRepaint
(
CustomClipper
<
T
>
oldClipper
);
...
...
@@ -995,7 +997,10 @@ enum DecorationPosition {
/// Paints a [Decoration] either before or after its child paints.
class
RenderDecoratedBox
extends
RenderProxyBox
{
/// Creates a decorated box.
///
/// Both the [decoration] and the [position] arguments are required. By
/// default the decoration paints behind the child.
RenderDecoratedBox
({
Decoration
decoration
,
DecorationPosition
position:
DecorationPosition
.
background
,
...
...
@@ -1010,6 +1015,8 @@ class RenderDecoratedBox extends RenderProxyBox {
BoxPainter
_painter
;
/// What decoration to paint.
///
/// Commonly a [BoxDecoration].
Decoration
get
decoration
=>
_decoration
;
Decoration
_decoration
;
void
set
decoration
(
Decoration
newDecoration
)
{
...
...
@@ -1023,7 +1030,7 @@ class RenderDecoratedBox extends RenderProxyBox {
markNeedsPaint
();
}
/// Whe
re to paint the box decoration
.
/// Whe
ther to paint the box decoration behind or in front of the child
.
DecorationPosition
get
position
=>
_position
;
DecorationPosition
_position
;
void
set
position
(
DecorationPosition
newPosition
)
{
...
...
@@ -1118,7 +1125,7 @@ class RenderTransform extends RenderProxyBox {
/// The alignment of the origin, relative to the size of the box.
///
/// This is equivalent to setting an origin based on the size of the box.
/// If it is specifi
c
ed at the same time as an offset, both are applied.
/// If it is specified at the same time as an offset, both are applied.
FractionalOffset
get
alignment
=>
_alignment
;
FractionalOffset
_alignment
;
void
set
alignment
(
FractionalOffset
newAlignment
)
{
...
...
packages/flutter/lib/src/rendering/shifted_box.dart
View file @
8ce2f859
...
...
@@ -221,7 +221,7 @@ class RenderPadding extends RenderShiftedBox {
abstract
class
RenderAligningShiftedBox
extends
RenderShiftedBox
{
RenderAligningShiftedBox
({
RenderBox
child
,
FractionalOffset
alignment:
const
FractionalOffset
(
0.5
,
0.5
)
FractionalOffset
alignment:
FractionalOffset
.
center
})
:
_alignment
=
alignment
,
super
(
child
)
{
assert
(
alignment
!=
null
&&
alignment
.
dx
!=
null
&&
alignment
.
dy
!=
null
);
...
...
@@ -277,7 +277,7 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox {
///
/// For example, to align a box at the bottom right, you would pass this box a
/// tight constraint that is bigger than the child's natural size,
/// with an alignment of [
const FractionalOffset(1.0, 1.0)
].
/// with an alignment of [
FractionalOffset.bottomRight
].
///
/// By default, sizes to be as big as possible in both axes. If either axis is
/// unconstrained, then in that direction it will be sized to fit the child's
...
...
@@ -288,7 +288,7 @@ class RenderPositionedBox extends RenderAligningShiftedBox {
RenderBox
child
,
double
widthFactor
,
double
heightFactor
,
FractionalOffset
alignment:
const
FractionalOffset
(
0.5
,
0.5
)
FractionalOffset
alignment:
FractionalOffset
.
center
})
:
_widthFactor
=
widthFactor
,
_heightFactor
=
heightFactor
,
super
(
child:
child
,
alignment:
alignment
)
{
...
...
@@ -432,7 +432,7 @@ class RenderConstrainedOverflowBox extends RenderAligningShiftedBox {
double
maxWidth
,
double
minHeight
,
double
maxHeight
,
FractionalOffset
alignment:
const
FractionalOffset
(
0.5
,
0.5
)
FractionalOffset
alignment:
FractionalOffset
.
center
})
:
_minWidth
=
minWidth
,
_maxWidth
=
maxWidth
,
_minHeight
=
minHeight
,
...
...
@@ -548,7 +548,7 @@ class RenderSizedOverflowBox extends RenderAligningShiftedBox {
RenderSizedOverflowBox
({
RenderBox
child
,
Size
requestedSize
,
FractionalOffset
alignment:
const
FractionalOffset
(
0.5
,
0.5
)
FractionalOffset
alignment:
FractionalOffset
.
center
})
:
_requestedSize
=
requestedSize
,
super
(
child:
child
,
alignment:
alignment
)
{
assert
(
requestedSize
!=
null
);
...
...
@@ -620,7 +620,7 @@ class RenderFractionallySizedOverflowBox extends RenderAligningShiftedBox {
RenderBox
child
,
double
widthFactor
,
double
heightFactor
,
FractionalOffset
alignment:
const
FractionalOffset
(
0.5
,
0.5
)
FractionalOffset
alignment:
FractionalOffset
.
center
})
:
_widthFactor
=
widthFactor
,
_heightFactor
=
heightFactor
,
super
(
child:
child
,
alignment:
alignment
)
{
...
...
packages/flutter/lib/src/rendering/stack.dart
View file @
8ce2f859
...
...
@@ -133,7 +133,7 @@ class RelativeRect {
String
toString
()
=>
"RelativeRect.fromLTRB(
${left?.toStringAsFixed(1)}
,
${top?.toStringAsFixed(1)}
,
${right?.toStringAsFixed(1)}
,
${bottom?.toStringAsFixed(1)}
)"
;
}
/// Parent data for use with [RenderStack]
/// Parent data for use with [RenderStack]
.
class
StackParentData
extends
ContainerBoxParentDataMixin
<
RenderBox
>
{
/// The distance by which the child's top edge is inset from the top of the stack.
double
top
;
...
...
@@ -437,6 +437,10 @@ abstract class RenderStackBase extends RenderBox
/// edge of the stack. If the child extends beyond the bounds of the
/// stack, the stack will clip the child's painting to the bounds of
/// the stack.
///
/// See also:
///
/// * [RenderFlow]
class
RenderStack
extends
RenderStackBase
{
RenderStack
({
List
<
RenderBox
>
children
,
...
...
packages/flutter/lib/src/services/image_resource.dart
View file @
8ce2f859
...
...
@@ -14,7 +14,7 @@ import 'package:flutter/foundation.dart';
class
ImageInfo
{
/// Creates an [ImageInfo] object for the given image and scale.
///
/// Both the image and the scale must
be non-
null.
/// Both the image and the scale must
not be
null.
ImageInfo
({
this
.
image
,
this
.
scale
:
1.0
})
{
assert
(
image
!=
null
);
assert
(
scale
!=
null
);
...
...
packages/flutter/lib/src/widgets/basic.dart
View file @
8ce2f859
...
...
@@ -21,6 +21,8 @@ export 'package:flutter/rendering.dart' show
CustomPainter
,
FixedColumnCountGridDelegate
,
FlexDirection
,
FlowDelegate
,
FlowPaintingContext
,
FractionalOffsetTween
,
GridDelegate
,
GridDelegateWithInOrderChildPlacement
,
...
...
@@ -165,7 +167,7 @@ class DecoratedBox extends SingleChildRenderObjectWidget {
/// Commonly a [BoxDecoration].
final
Decoration
decoration
;
/// Whe
re to paint the box decoration
.
/// Whe
ther to paint the box decoration behind or in front of the child
.
final
DecorationPosition
position
;
@override
...
...
@@ -431,16 +433,25 @@ class Padding extends SingleChildRenderObjectWidget {
///
/// For example, to align a box at the bottom right, you would pass this box a
/// tight constraint that is bigger than the child's natural size,
/// with an alignment of [
const FractionalOffset(1.0, 1.0)
].
/// with an alignment of [
FractionalOffset.bottomRight
].
///
/// By default, sizes to be as big as possible in both axes. If either axis is
/// unconstrained, then in that direction it will be sized to fit the child's
/// dimensions. Using widthFactor and heightFactor you can force this latter
/// behavior in all cases.
///
/// See also:
///
/// * [CustomSingleChildLayout]
/// * [Center] (which is the same as [Align] but with the [alignment] always
/// set to [FractionalOffset.center])
class
Align
extends
SingleChildRenderObjectWidget
{
/// Creates an alignment widget.
///
/// The alignment defaults to [FractionalOffset.center].
Align
({
Key
key
,
this
.
alignment
:
const
FractionalOffset
(
0.5
,
0.5
)
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
widthFactor
,
this
.
heightFactor
,
Widget
child
...
...
@@ -494,6 +505,10 @@ class Align extends SingleChildRenderObjectWidget {
}
/// Centers its child within itself.
///
/// See also:
///
/// * [Align]
class
Center
extends
Align
{
Center
({
Key
key
,
double
widthFactor
,
double
heightFactor
,
Widget
child
})
:
super
(
key:
key
,
widthFactor:
widthFactor
,
heightFactor:
heightFactor
,
child:
child
);
...
...
@@ -505,7 +520,17 @@ class Center extends Align {
/// decide where to position the child. The delegate can also determine the size
/// of the parent, but the size of the parent cannot depend on the size of the
/// child.
///
/// See also:
///
/// * [SingleChildLayoutDelegate]
/// * [Align] (which positions a single child according to a [FractionalOffset])
/// * [CustomMultiChildLayout] (which uses a delegate to position multiple
/// children)
class
CustomSingleChildLayout
extends
SingleChildRenderObjectWidget
{
/// Creates a custom single child layout.
///
/// The delegate argument must not be null.
CustomSingleChildLayout
({
Key
key
,
this
.
delegate
,
...
...
@@ -514,6 +539,7 @@ class CustomSingleChildLayout extends SingleChildRenderObjectWidget {
assert
(
delegate
!=
null
);
}
/// The delegate that controls the layout of the child.
final
SingleChildLayoutDelegate
delegate
;
@override
...
...
@@ -527,6 +553,9 @@ class CustomSingleChildLayout extends SingleChildRenderObjectWidget {
/// Metadata for identifying children in a [CustomMultiChildLayout].
class
LayoutId
extends
ParentDataWidget
<
CustomMultiChildLayout
>
{
/// Marks a child with a layout identifier.
///
/// Both the child and the id arguments must not be null.
LayoutId
({
Key
key
,
Widget
child
,
...
...
@@ -566,7 +595,17 @@ const List<Widget> _emptyWidgetList = const <Widget>[];
/// decide where to position each child. The delegate can also determine the
/// size of the parent, but the size of the parent cannot depend on the sizes of
/// the children.
///
/// See also:
///
/// * [MultiChildLayoutDelegate]
/// * [CustomSingleChildLayout]
/// * [Stack]
/// * [Flow]
class
CustomMultiChildLayout
extends
MultiChildRenderObjectWidget
{
/// Creates a custom multi-child layout.
///
/// The delegate argument must not be null.
CustomMultiChildLayout
({
Key
key
,
List
<
Widget
>
children:
_emptyWidgetList
,
...
...
@@ -667,7 +706,7 @@ class ConstrainedBox extends SingleChildRenderObjectWidget {
class
FractionallySizedBox
extends
SingleChildRenderObjectWidget
{
FractionallySizedBox
({
Key
key
,
this
.
alignment
:
const
FractionalOffset
(
0.5
,
0.5
)
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
width
,
this
.
height
,
Widget
child
...
...
@@ -772,7 +811,7 @@ class LimitedBox extends SingleChildRenderObjectWidget {
class
OverflowBox
extends
SingleChildRenderObjectWidget
{
OverflowBox
({
Key
key
,
this
.
alignment
:
const
FractionalOffset
(
0.5
,
0.5
)
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
minWidth
,
this
.
maxWidth
,
this
.
minHeight
,
...
...
@@ -844,7 +883,7 @@ class OverflowBox extends SingleChildRenderObjectWidget {
class
SizedOverflowBox
extends
SingleChildRenderObjectWidget
{
SizedOverflowBox
({
Key
key
,
this
.
alignment
:
const
FractionalOffset
(
0.5
,
0.5
)
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
size
,
Widget
child
})
:
super
(
key:
key
,
child:
child
)
{
...
...
@@ -1265,11 +1304,18 @@ abstract class StackRenderObjectWidgetBase extends MultiChildRenderObjectWidget
/// For more details about the stack layout algorithm, see
/// [RenderStack]. To control the position of child widgets, see the
/// [Positioned] widget.
///
/// See also:
///
/// * [Flow]
/// * [Align] (which positions a single child according to a [FractionalOffset])
/// * [CustomSingleChildLayout]
/// * [CustomMultiChildLayout]
class
Stack
extends
StackRenderObjectWidgetBase
{
Stack
({
Key
key
,
List
<
Widget
>
children:
_emptyWidgetList
,
this
.
alignment
:
const
FractionalOffset
(
0.0
,
0.0
)
this
.
alignment
:
FractionalOffset
.
topLeft
})
:
super
(
key:
key
,
children:
children
);
/// How to align the non-positioned children in the stack.
...
...
@@ -1289,7 +1335,7 @@ class IndexedStack extends StackRenderObjectWidgetBase {
IndexedStack
({
Key
key
,
List
<
Widget
>
children:
_emptyWidgetList
,
this
.
alignment
:
const
FractionalOffset
(
0.0
,
0.0
)
,
this
.
alignment
:
FractionalOffset
.
topLeft
,
this
.
index
:
0
})
:
super
(
key:
key
,
children:
children
)
{
assert
(
index
!=
null
);
...
...
@@ -1718,6 +1764,71 @@ class Flexible extends ParentDataWidget<Flex> {
}
}
/// Implements the flow layout algorithm.
///
/// Flow layouts are optimized for repositioning children using transformation
/// matrices.
///
/// The flow container is sized independently from the children by the
/// [FlowDelegate.getSize] function of the delegate. The children are then sized
/// independently given the constraints from the
/// [FlowDelegate.getConstraintsForChild] function.
///
/// Rather than positioning the children during layout, the children are
/// positioned using transformation matrices during the paint phase using the
/// matrices from the [FlowDelegate.paintChildren] function. The children can be
/// repositioned efficiently by simply repainting the flow.
///
/// The most efficient way to trigger a repaint of the flow is to supply a
/// repaint argument to the constructor of the [FlowDelegate]. The flow will
/// listen to this animation and repaint whenever the animation ticks, avoiding
/// both the build and layout phases of the pipeline.
///
/// See also:
///
/// * [FlowDelegate]
/// * [Stack]
/// * [CustomSingleChildLayout]
/// * [CustomMultiChildLayout]
class
Flow
extends
MultiChildRenderObjectWidget
{
/// Creates a flow layout.
///
/// Wraps each of the given children in a [RepaintBoundary] to avoid
/// repainting the children when the flow repaints.
Flow
({
Key
key
,
List
<
Widget
>
children:
_emptyWidgetList
,
this
.
delegate
})
:
super
(
key:
key
,
children:
RepaintBoundary
.
wrapAll
(
children
))
{
assert
(
delegate
!=
null
);
}
/// Creates a flow layout.
///
/// Does not wrap the given children in repaint boundaries, unlike the default
/// constructor. Useful when the child is trivial to paint or already contains
/// a repaint boundary.
Flow
.
unwrapped
({
Key
key
,
List
<
Widget
>
children:
_emptyWidgetList
,
this
.
delegate
})
:
super
(
key:
key
,
children:
children
)
{
assert
(
delegate
!=
null
);
}
/// The delegate that controls the transformation matrices of the children.
final
FlowDelegate
delegate
;
@override
RenderFlow
createRenderObject
(
BuildContext
context
)
=>
new
RenderFlow
(
delegate:
delegate
);
@override
void
updateRenderObject
(
BuildContext
context
,
RenderFlow
renderObject
)
{
renderObject
..
delegate
=
delegate
;
}
}
/// A paragraph of rich text.
///
/// The [RichText] widget displays text that uses multiple different styles. The
...
...
@@ -2571,6 +2682,13 @@ class RepaintBoundary extends SingleChildRenderObjectWidget {
return
new
RepaintBoundary
(
key:
key
,
child:
child
);
}
static
List
<
RepaintBoundary
>
wrapAll
(
List
<
Widget
>
widgets
)
{
List
<
RepaintBoundary
>
result
=
new
List
<
RepaintBoundary
>(
widgets
.
length
);
for
(
int
i
=
0
;
i
<
result
.
length
;
++
i
)
result
[
i
]
=
new
RepaintBoundary
.
wrap
(
widgets
[
i
],
i
);
return
result
;
}
@override
RenderRepaintBoundary
createRenderObject
(
BuildContext
context
)
=>
new
RenderRepaintBoundary
();
}
...
...
packages/flutter/test/widget/flow_test.dart
0 → 100644
View file @
8ce2f859
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:test/test.dart'
;
class
TestFlowDelegate
extends
FlowDelegate
{
TestFlowDelegate
({
Animation
<
double
>
startOffset
})
:
startOffset
=
startOffset
,
super
(
repaint:
startOffset
);
final
Animation
<
double
>
startOffset
;
@override
BoxConstraints
getConstraintsForChild
(
int
i
,
BoxConstraints
constraints
)
{
return
constraints
.
loosen
();
}
@override
void
paintChildren
(
FlowPaintingContext
context
)
{
double
dy
=
startOffset
.
value
;
for
(
int
i
=
0
;
i
<
context
.
childCount
;
++
i
)
{
context
.
paintChild
(
i
,
transform:
new
Matrix4
.
translationValues
(
0.0
,
dy
,
0.0
));
dy
+=
0.75
*
context
.
getChildSize
(
i
).
height
;
}
}
@override
bool
shouldRepaint
(
TestFlowDelegate
oldDelegate
)
=>
startOffset
==
oldDelegate
.
startOffset
;
}
void
main
(
)
{
test
(
'Flow control test'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
AnimationController
startOffset
=
new
AnimationController
.
unbounded
();
List
<
int
>
log
=
<
int
>[];
Widget
buildBox
(
int
i
)
{
return
new
GestureDetector
(
onTap:
()
{
log
.
add
(
i
);
},
child:
new
Container
(
width:
100.0
,
height:
100.0
,
decoration:
const
BoxDecoration
(
backgroundColor:
const
Color
(
0xFF0000FF
)
),
child:
new
Text
(
'
$i
'
)
)
);
}
tester
.
pumpWidget
(
new
Flow
(
delegate:
new
TestFlowDelegate
(
startOffset:
startOffset
),
children:
<
Widget
>[
buildBox
(
0
),
buildBox
(
1
),
buildBox
(
2
),
buildBox
(
3
),
buildBox
(
4
),
buildBox
(
5
),
buildBox
(
6
),
]
)
);
tester
.
tap
(
find
.
text
(
'0'
));
expect
(
log
,
equals
([
0
]));
tester
.
tap
(
find
.
text
(
'1'
));
expect
(
log
,
equals
([
0
,
1
]));
tester
.
tap
(
find
.
text
(
'2'
));
expect
(
log
,
equals
([
0
,
1
,
2
]));
log
.
clear
();
tester
.
tapAt
(
new
Point
(
20.0
,
90.0
));
expect
(
log
,
equals
([
1
]));
startOffset
.
value
=
50.0
;
tester
.
pump
();
log
.
clear
();
tester
.
tapAt
(
new
Point
(
20.0
,
90.0
));
expect
(
log
,
equals
([
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