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
7bf3cb41
Commit
7bf3cb41
authored
Jan 02, 2016
by
Adam Barth
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1061 from abarth/generalize_grid_layout
Generalize grid layout
parents
a5925149
46a178dc
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
478 additions
and
91 deletions
+478
-91
render_grid.dart
examples/rendering/render_grid.dart
+4
-1
media_query.dart
examples/widgets/media_query.dart
+3
-3
edge_dims.dart
packages/flutter/lib/src/painting/edge_dims.dart
+11
-2
box.dart
packages/flutter/lib/src/rendering/box.dart
+1
-1
grid.dart
packages/flutter/lib/src/rendering/grid.dart
+342
-75
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+112
-7
grid_test.dart
packages/flutter/test/rendering/grid_test.dart
+5
-2
No files found.
examples/rendering/render_grid.dart
View file @
7bf3cb41
...
...
@@ -20,7 +20,10 @@ Color randomColor() {
RenderBox
buildGridExample
(
)
{
List
<
RenderBox
>
children
=
new
List
<
RenderBox
>.
generate
(
30
,
(
_
)
=>
new
RenderSolidColorBox
(
randomColor
()));
return
new
RenderGrid
(
children:
children
,
maxChildExtent:
100.0
);
return
new
RenderGrid
(
children:
children
,
delegate:
new
MaxTileWidthGridDelegate
(
maxTileWidth:
100.0
)
);
}
main
()
=>
new
RenderingFlutterBinding
(
root:
buildGridExample
());
examples/widgets/media_query.dart
View file @
7bf3cb41
...
...
@@ -62,7 +62,7 @@ class AdaptiveItem {
}
class
MediaQueryExample
extends
StatelessComponent
{
static
const
double
_max
ChildExtent
=
150.0
;
static
const
double
_max
TileWidth
=
150.0
;
static
const
double
_gridViewBreakpoint
=
450.0
;
Widget
_buildBody
(
BuildContext
context
)
{
...
...
@@ -78,9 +78,9 @@ class MediaQueryExample extends StatelessComponent {
}
else
{
return
new
Block
(
<
Widget
>[
new
Grid
(
new
MaxTileWidth
Grid
(
items
.
map
((
AdaptiveItem
item
)
=>
item
.
toCard
()).
toList
(),
max
ChildExtent:
_maxChildExtent
max
TileWidth:
_maxTileWidth
)
]
);
...
...
packages/flutter/lib/src/painting/edge_dims.dart
View file @
7bf3cb41
...
...
@@ -43,8 +43,17 @@ class EdgeDims {
/// Whether every dimension is non-negative.
bool
get
isNonNegative
=>
top
>=
0.0
&&
right
>=
0.0
&&
bottom
>=
0.0
&&
left
>=
0.0
;
/// The size that this edge dims would occupy with an empty interior.
ui
.
Size
get
collapsedSize
=>
new
ui
.
Size
(
left
+
right
,
top
+
bottom
);
/// The total offset in the vertical direction.
double
get
horizontal
=>
left
+
right
;
/// The total offset in the horizontal direction.
double
get
vertical
=>
top
+
bottom
;
/// The size that this EdgeDims would occupy with an empty interior.
ui
.
Size
get
collapsedSize
=>
new
ui
.
Size
(
horizontal
,
vertical
);
/// An EdgeDims with top and bottom as well as left and right flipped.
EdgeDims
get
flipped
=>
new
EdgeDims
.
TRBL
(
bottom
,
left
,
top
,
right
);
ui
.
Rect
inflateRect
(
ui
.
Rect
rect
)
{
return
new
ui
.
Rect
.
fromLTRB
(
rect
.
left
-
left
,
rect
.
top
-
top
,
rect
.
right
+
right
,
rect
.
bottom
+
bottom
);
...
...
packages/flutter/lib/src/rendering/box.dart
View file @
7bf3cb41
...
...
@@ -374,7 +374,7 @@ abstract class RenderBox extends RenderObject {
return
constraints
.
constrainWidth
(
0.0
);
}
/// Return the minimum height that this box could be without failing to
render
/// Return the minimum height that this box could be without failing to
paint
/// its contents within itself.
///
/// Override in subclasses that implement [performLayout].
...
...
packages/flutter/lib/src/rendering/grid.dart
View file @
7bf3cb41
...
...
@@ -5,67 +5,325 @@
import
'box.dart'
;
import
'object.dart'
;
class
_GridMetrics
{
// Grid is width-in, height-out. We fill the max width and adjust height
// accordingly.
factory
_GridMetrics
({
double
width
,
int
childCount
,
double
maxChildExtent
})
{
assert
(
width
!=
null
);
assert
(
childCount
!=
null
);
assert
(
maxChildExtent
!=
null
);
double
childExtent
=
maxChildExtent
;
int
childrenPerRow
=
(
width
/
childExtent
).
floor
();
// If the child extent divides evenly into the width use that, otherwise + 1
if
(
width
/
childExtent
!=
childrenPerRow
.
toDouble
())
childrenPerRow
+=
1
;
double
totalPadding
=
0.0
;
if
(
childrenPerRow
*
childExtent
>
width
)
{
// TODO(eseidel): We should snap to pixel bounderies.
childExtent
=
width
/
childrenPerRow
;
}
else
{
totalPadding
=
width
-
(
childrenPerRow
*
childExtent
);
}
double
childPadding
=
totalPadding
/
(
childrenPerRow
+
1.0
);
int
rowCount
=
(
childCount
/
childrenPerRow
).
ceil
();
double
height
=
childPadding
*
(
rowCount
+
1
)
+
(
childExtent
*
rowCount
);
Size
childSize
=
new
Size
(
childExtent
,
childExtent
);
Size
size
=
new
Size
(
width
,
height
);
return
new
_GridMetrics
.
_
(
size
,
childSize
,
childrenPerRow
,
childPadding
,
rowCount
);
}
const
_GridMetrics
.
_
(
this
.
size
,
this
.
childSize
,
this
.
childrenPerRow
,
this
.
childPadding
,
this
.
rowCount
);
final
Size
size
;
final
Size
childSize
;
final
int
childrenPerRow
;
// aka columnCount
final
double
childPadding
;
final
int
rowCount
;
bool
_debugIsMonotonic
(
List
<
double
>
offsets
)
{
bool
result
=
true
;
assert
(()
{
double
current
=
0.0
;
for
(
double
offset
in
offsets
)
{
if
(
current
>
offset
)
{
result
=
false
;
break
;
}
current
=
offset
;
}
return
true
;
});
return
result
;
}
List
<
double
>
_generateRegularOffsets
(
int
count
,
double
size
)
{
int
length
=
count
+
1
;
List
<
double
>
result
=
new
List
<
double
>(
length
);
for
(
int
i
=
0
;
i
<
length
;
++
i
)
result
[
i
]
=
i
*
size
;
return
result
;
}
class
GridSpecification
{
/// Creates a grid specification from an explicit list of offsets.
GridSpecification
.
fromOffsets
({
this
.
columnOffsets
,
this
.
rowOffsets
,
this
.
padding
:
EdgeDims
.
zero
})
{
assert
(
_debugIsMonotonic
(
columnOffsets
));
assert
(
_debugIsMonotonic
(
rowOffsets
));
assert
(
padding
!=
null
);
}
/// Creates a grid specification containing a certain number of equally sized tiles.
GridSpecification
.
fromRegularTiles
({
double
tileWidth
,
double
tileHeight
,
int
columnCount
,
int
rowCount
,
this
.
padding
:
EdgeDims
.
zero
})
:
columnOffsets
=
_generateRegularOffsets
(
columnCount
,
tileWidth
),
rowOffsets
=
_generateRegularOffsets
(
rowCount
,
tileHeight
)
{
assert
(
_debugIsMonotonic
(
columnOffsets
));
assert
(
_debugIsMonotonic
(
rowOffsets
));
assert
(
padding
!=
null
);
}
/// The offsets of the column boundaries in the grid.
///
/// The first offset is the offset of the left edge of the left-most column
/// from the left edge of the interior of the grid's padding (usually 0.0).
/// The last offset is the offset of the right edge of the right-most column
/// from the left edge of the interior of the grid's padding.
///
/// If there are n columns in the grid, there should be n + 1 entries in this
/// list (because there's an entry before the first column and after the last
/// column).
final
List
<
double
>
columnOffsets
;
/// The offsets of the row boundaries in the grid.
///
/// The first offset is the offset of the top edge of the top-most row from
/// the top edge of the interior of the grid's padding (usually 0.0). The
/// last offset is the offset of the bottom edge of the bottom-most column
/// from the top edge of the interior of the grid's padding.
///
/// If there are n rows in the grid, there should be n + 1 entries in this
/// list (because there's an entry before the first row and after the last
/// row).
final
List
<
double
>
rowOffsets
;
/// The interior padding of the grid.
///
/// The grid's size encloses the rows and columns and is then inflated by the
/// padding.
final
EdgeDims
padding
;
/// The size of the grid.
Size
get
gridSize
=>
new
Size
(
columnOffsets
.
last
+
padding
.
horizontal
,
rowOffsets
.
last
+
padding
.
vertical
);
}
/// Where to place a child within a grid.
class
GridChildPlacement
{
GridChildPlacement
({
this
.
column
,
this
.
row
,
this
.
columnSpan
:
1
,
this
.
rowSpan
:
1
,
this
.
padding
:
EdgeDims
.
zero
})
{
assert
(
column
!=
null
);
assert
(
row
!=
null
);
assert
(
columnSpan
!=
null
);
assert
(
rowSpan
!=
null
);
assert
(
padding
!=
null
);
}
/// The column in which to place the child.
final
int
column
;
/// The row in which to place the child.
final
int
row
;
/// How many columns the child should span.
final
int
columnSpan
;
/// How many rows the child should span.
final
int
rowSpan
;
/// How much the child should be inset from the column and row boundaries.
final
EdgeDims
padding
;
}
/// An abstract interface to control the layout of a [RenderGrid].
abstract
class
GridDelegate
{
/// Override this function to control size of the columns and rows.
GridSpecification
getGridSpecification
(
BoxConstraints
constraints
,
int
childCount
);
/// Override this function to control where children are placed in the grid.
GridChildPlacement
getChildPlacement
(
GridSpecification
specification
,
int
index
,
Object
placementData
);
/// Override this method to return true when the children need to be laid out.
bool
shouldRelayout
(
GridDelegate
oldDelegate
)
=>
true
;
Size
_getGridSize
(
BoxConstraints
constraints
,
int
childCount
)
{
return
getGridSpecification
(
constraints
,
childCount
).
gridSize
;
}
/// Returns the minimum width that this grid could be without failing to paint
/// its contents within itself.
double
getMinIntrinsicWidth
(
BoxConstraints
constraints
,
int
childCount
)
{
return
constraints
.
constrainWidth
(
_getGridSize
(
constraints
,
childCount
).
width
);
}
/// Returns the smallest width beyond which increasing the width never
/// decreases the height.
double
getMaxIntrinsicWidth
(
BoxConstraints
constraints
,
int
childCount
)
{
return
constraints
.
constrainWidth
(
_getGridSize
(
constraints
,
childCount
).
width
);
}
/// Return the minimum height that this grid could be without failing to paint
/// its contents within itself.
double
getMinIntrinsicHeight
(
BoxConstraints
constraints
,
int
childCount
)
{
return
constraints
.
constrainHeight
(
_getGridSize
(
constraints
,
childCount
).
height
);
}
/// Returns the smallest height beyond which increasing the height never
/// decreases the width.
double
getMaxIntrinsicHeight
(
BoxConstraints
constraints
,
int
childCount
)
{
return
constraints
.
constrainHeight
(
_getGridSize
(
constraints
,
childCount
).
height
);
}
}
/// A [GridDelegate] the places its children in order throughout the grid.
abstract
class
GridDelegateWithInOrderChildPlacement
extends
GridDelegate
{
GridDelegateWithInOrderChildPlacement
({
this
.
padding
:
EdgeDims
.
zero
});
/// The amount of padding to apply to each child.
final
EdgeDims
padding
;
GridChildPlacement
getChildPlacement
(
GridSpecification
specification
,
int
index
,
Object
placementData
)
{
int
columnCount
=
specification
.
columnOffsets
.
length
-
1
;
return
new
GridChildPlacement
(
column:
index
%
columnCount
,
row:
index
~/
columnCount
,
padding:
padding
);
}
bool
shouldRelayout
(
GridDelegateWithInOrderChildPlacement
oldDelegate
)
{
return
padding
!=
oldDelegate
.
padding
;
}
}
/// A [GridDelegate] that divides the grid's width evenly amount a fixed number of columns.
class
FixedColumnCountGridDelegate
extends
GridDelegateWithInOrderChildPlacement
{
FixedColumnCountGridDelegate
({
this
.
columnCount
,
this
.
tileAspectRatio
:
1.0
,
EdgeDims
padding:
EdgeDims
.
zero
})
:
super
(
padding:
padding
);
/// The number of columns in the grid.
final
int
columnCount
;
/// The ratio of the width to the height of each tile in the grid.
final
double
tileAspectRatio
;
GridSpecification
getGridSpecification
(
BoxConstraints
constraints
,
int
childCount
)
{
assert
(
constraints
.
maxWidth
<
double
.
INFINITY
);
int
rowCount
=
(
childCount
/
columnCount
).
ceil
();
double
tileWidth
=
constraints
.
maxWidth
/
columnCount
;
double
tileHeight
=
tileWidth
/
tileAspectRatio
;
return
new
GridSpecification
.
fromRegularTiles
(
tileWidth:
tileWidth
,
tileHeight:
tileHeight
,
columnCount:
columnCount
,
rowCount:
rowCount
,
padding:
padding
.
flipped
);
}
bool
shouldRelayout
(
FixedColumnCountGridDelegate
oldDelegate
)
{
return
columnCount
!=
oldDelegate
.
columnCount
||
tileAspectRatio
!=
oldDelegate
.
tileAspectRatio
||
super
.
shouldRelayout
(
oldDelegate
);
}
double
getMinIntrinsicWidth
(
BoxConstraints
constraints
,
int
childCount
)
{
return
constraints
.
constrainWidth
(
0.0
);
}
double
getMaxIntrinsicWidth
(
BoxConstraints
constraints
,
int
childCount
)
{
return
constraints
.
constrainWidth
(
0.0
);
}
}
/// A [GridDelegate] that fills the width with a variable number of tiles.
///
/// This delegate will select a tile width that is as large as possible subject
/// to the following conditions:
///
/// - The tile width evenly divides the width of the grid.
/// - The tile width is at most [maxTileWidth].
///
class
MaxTileWidthGridDelegate
extends
GridDelegateWithInOrderChildPlacement
{
MaxTileWidthGridDelegate
({
this
.
maxTileWidth
,
this
.
tileAspectRatio
:
1.0
,
EdgeDims
padding:
EdgeDims
.
zero
})
:
super
(
padding:
padding
);
/// The maximum width of a tile in the grid.
final
double
maxTileWidth
;
/// The ratio of the width to the height of each tile in the grid.
final
double
tileAspectRatio
;
GridSpecification
getGridSpecification
(
BoxConstraints
constraints
,
int
childCount
)
{
assert
(
constraints
.
maxWidth
<
double
.
INFINITY
);
double
gridWidth
=
constraints
.
maxWidth
;
int
columnCount
=
(
gridWidth
/
maxTileWidth
).
ceil
();
int
rowCount
=
(
childCount
/
columnCount
).
ceil
();
double
tileWidth
=
gridWidth
/
columnCount
;
double
tileHeight
=
tileWidth
/
tileAspectRatio
;
return
new
GridSpecification
.
fromRegularTiles
(
tileWidth:
tileWidth
,
tileHeight:
tileHeight
,
columnCount:
columnCount
,
rowCount:
rowCount
,
padding:
padding
.
flipped
);
}
bool
shouldRelayout
(
MaxTileWidthGridDelegate
oldDelegate
)
{
return
maxTileWidth
!=
oldDelegate
.
maxTileWidth
||
tileAspectRatio
!=
oldDelegate
.
tileAspectRatio
||
super
.
shouldRelayout
(
oldDelegate
);
}
double
getMinIntrinsicWidth
(
BoxConstraints
constraints
,
int
childCount
)
{
return
constraints
.
constrainWidth
(
0.0
);
}
double
getMaxIntrinsicWidth
(
BoxConstraints
constraints
,
int
childCount
)
{
return
constraints
.
constrainWidth
(
maxTileWidth
*
childCount
);
}
}
/// Parent data for use with [RenderGrid]
class
GridParentData
extends
ContainerBoxParentDataMixin
<
RenderBox
>
{}
class
GridParentData
extends
ContainerBoxParentDataMixin
<
RenderBox
>
{
/// Opaque data passed to the getChildPlacement method of the grid's [GridDelegate].
Object
placementData
;
void
merge
(
GridParentData
other
)
{
if
(
other
.
placementData
!=
null
)
placementData
=
other
.
placementData
;
super
.
merge
(
other
);
}
String
toString
()
=>
'
${super.toString()}
; placementData=
$placementData
'
;
}
/// Implements the grid layout algorithm
///
/// In grid layout, children are arranged into rows and collumns in on a two
/// dimensional grid. The grid determines how many children will be placed in
/// each row by making the children as wide as possible while still respecting
/// the given [maxChildExtent].
/// In grid layout, children are arranged into rows and columns in on a two
/// dimensional grid. The [GridDelegate] determines how to arrange the
/// children on the grid.
///
/// The arrangment of rows and columns in the grid cannot depend on the contents
/// of the tiles in the grid, which makes grid layout most useful for images and
/// card-like layouts rather than for document-like layouts that adjust to the
/// amount of text contained in the tiles.
///
/// Additionally, grid layout materializes all of its children, which makes it
/// most useful for grids containing a moderate number of tiles.
class
RenderGrid
extends
RenderBox
with
ContainerRenderObjectMixin
<
RenderBox
,
GridParentData
>,
RenderBoxContainerDefaultsMixin
<
RenderBox
,
GridParentData
>
{
RenderGrid
({
List
<
RenderBox
>
children
,
double
maxChildExtent
})
{
RenderGrid
({
List
<
RenderBox
>
children
,
GridDelegate
delegate
})
:
_delegate
=
delegate
{
assert
(
delegate
!=
null
);
addAll
(
children
);
_maxChildExtent
=
maxChildExtent
;
}
double
_maxChildExtent
;
bool
_hasVisualOverflow
=
false
;
double
get
maxChildExtent
=>
_maxChildExtent
;
void
set
maxChildExtent
(
double
value
)
{
if
(
_maxChildExtent
!=
value
)
{
_maxChildExtent
=
value
;
/// The delegate that controls the layout of the children.
GridDelegate
get
delegate
=>
_delegate
;
GridDelegate
_delegate
;
void
set
delegate
(
GridDelegate
newDelegate
)
{
assert
(
newDelegate
!=
null
);
if
(
_delegate
==
newDelegate
)
return
;
if
(
newDelegate
.
runtimeType
!=
_delegate
.
runtimeType
||
newDelegate
.
shouldRelayout
(
_delegate
))
markNeedsLayout
();
}
_delegate
=
newDelegate
;
}
void
setupParentData
(
RenderBox
child
)
{
...
...
@@ -75,63 +333,72 @@ class RenderGrid extends RenderBox with ContainerRenderObjectMixin<RenderBox, Gr
double
getMinIntrinsicWidth
(
BoxConstraints
constraints
)
{
assert
(
constraints
.
isNormalized
);
// We can render at any width.
return
constraints
.
constrainWidth
(
0.0
);
return
_delegate
.
getMinIntrinsicWidth
(
constraints
,
childCount
);
}
double
getMaxIntrinsicWidth
(
BoxConstraints
constraints
)
{
assert
(
constraints
.
isNormalized
);
double
maxWidth
=
childCount
*
_maxChildExtent
;
return
constraints
.
constrainWidth
(
maxWidth
);
return
_delegate
.
getMaxIntrinsicWidth
(
constraints
,
childCount
);
}
double
getMinIntrinsicHeight
(
BoxConstraints
constraints
)
{
assert
(
constraints
.
isNormalized
);
double
desiredHeight
=
_computeMetrics
().
size
.
height
;
return
constraints
.
constrainHeight
(
desiredHeight
);
return
_delegate
.
getMinIntrinsicHeight
(
constraints
,
childCount
);
}
double
getMaxIntrinsicHeight
(
BoxConstraints
constraints
)
{
assert
(
constraints
.
isNormalized
);
return
getMinIntrinsicHeight
(
constraints
);
return
_delegate
.
getMaxIntrinsicHeight
(
constraints
,
childCount
);
}
double
computeDistanceToActualBaseline
(
TextBaseline
baseline
)
{
return
defaultComputeDistanceToHighestActualBaseline
(
baseline
);
}
_GridMetrics
_computeMetrics
()
{
return
new
_GridMetrics
(
width:
constraints
.
maxWidth
,
childCount:
childCount
,
maxChildExtent:
_maxChildExtent
);
}
GridSpecification
_specification
;
bool
_hasVisualOverflow
=
false
;
void
performLayout
()
{
// We could shrink-wrap our contents when infinite, but for now we don't.
assert
(
constraints
.
maxWidth
<
double
.
INFINITY
);
_GridMetrics
metrics
=
_computeMetrics
();
size
=
constraints
.
constrain
(
metrics
.
size
);
if
(
constraints
.
maxHeight
<
size
.
height
)
_specification
=
delegate
.
getGridSpecification
(
constraints
,
childCount
);
Size
gridSize
=
_specification
.
gridSize
;
size
=
constraints
.
constrain
(
gridSize
);
if
(
gridSize
.
width
>
size
.
width
||
gridSize
.
height
>
size
.
height
)
_hasVisualOverflow
=
true
;
int
row
=
0
;
int
column
=
0
;
double
gridTopPadding
=
_specification
.
padding
.
top
;
double
gridLeftPadding
=
_specification
.
padding
.
left
;
int
index
=
0
;
RenderBox
child
=
firstChild
;
while
(
child
!=
null
)
{
child
.
layout
(
new
BoxConstraints
.
tight
(
metrics
.
childSize
));
double
x
=
(
column
+
1
)
*
metrics
.
childPadding
+
(
column
*
metrics
.
childSize
.
width
);
double
y
=
(
row
+
1
)
*
metrics
.
childPadding
+
(
row
*
metrics
.
childSize
.
height
);
final
GridParentData
childParentData
=
child
.
parentData
;
childParentData
.
offset
=
new
Offset
(
x
,
y
);
column
+=
1
;
if
(
column
>=
metrics
.
childrenPerRow
)
{
row
+=
1
;
column
=
0
;
}
GridChildPlacement
placement
=
delegate
.
getChildPlacement
(
_specification
,
index
,
childParentData
.
placementData
);
assert
(
placement
.
column
>=
0
);
assert
(
placement
.
row
>=
0
);
assert
(
placement
.
column
+
placement
.
columnSpan
<
_specification
.
columnOffsets
.
length
);
assert
(
placement
.
row
+
placement
.
rowSpan
<
_specification
.
rowOffsets
.
length
);
double
tileLeft
=
_specification
.
columnOffsets
[
placement
.
column
]
+
gridLeftPadding
;
double
tileRight
=
_specification
.
columnOffsets
[
placement
.
column
+
placement
.
columnSpan
]
+
gridLeftPadding
;
double
tileTop
=
_specification
.
rowOffsets
[
placement
.
row
]
+
gridTopPadding
;
double
tileBottom
=
_specification
.
rowOffsets
[
placement
.
row
+
placement
.
rowSpan
]
+
gridTopPadding
;
double
childWidth
=
tileRight
-
tileLeft
-
placement
.
padding
.
horizontal
;
double
childHeight
=
tileBottom
-
tileTop
-
placement
.
padding
.
vertical
;
child
.
layout
(
new
BoxConstraints
(
minWidth:
childWidth
,
maxWidth:
childWidth
,
minHeight:
childHeight
,
maxHeight:
childHeight
));
childParentData
.
offset
=
new
Offset
(
tileLeft
+
placement
.
padding
.
left
,
tileTop
+
placement
.
padding
.
top
);
++
index
;
assert
(
child
.
parentData
==
childParentData
);
child
=
childParentData
.
nextSibling
;
...
...
packages/flutter/lib/src/widgets/basic.dart
View file @
7bf3cb41
...
...
@@ -33,6 +33,7 @@ export 'package:flutter/rendering.dart' show
FontWeight
,
FractionalOffset
,
Gradient
,
GridDelegate
,
HitTestBehavior
,
ImageFit
,
ImageRepeat
,
...
...
@@ -1125,21 +1126,125 @@ class Positioned extends ParentDataWidget<StackRenderObjectWidgetBase> {
}
}
abstract
class
GridRenderObjectWidgetBase
extends
MultiChildRenderObjectWidget
{
GridRenderObjectWidgetBase
({
List
<
Widget
>
children
,
Key
key
})
:
super
(
key:
key
,
children:
children
)
{
_delegate
=
createDelegate
();
}
GridDelegate
_delegate
;
/// The delegate that controls the layout of the children.
GridDelegate
createDelegate
();
RenderGrid
createRenderObject
()
=>
new
RenderGrid
(
delegate:
_delegate
);
void
updateRenderObject
(
RenderGrid
renderObject
,
GridRenderObjectWidgetBase
oldWidget
)
{
renderObject
.
delegate
=
_delegate
;
}
}
/// Uses the grid layout algorithm for its children.
///
/// For details about the grid layout algorithm, see [RenderGrid].
class
Grid
extends
MultiChildRenderObjectWidget
{
Grid
(
List
<
Widget
>
children
,
{
Key
key
,
this
.
maxChildExtent
})
class
CustomGrid
extends
GridRenderObjectWidgetBase
{
CustomGrid
(
List
<
Widget
>
children
,
{
Key
key
,
this
.
delegate
})
:
super
(
key:
key
,
children:
children
)
{
assert
(
maxChildExtent
!=
null
);
assert
(
delegate
!=
null
);
}
final
double
maxChildExtent
;
/// The delegate that controls the layout of the children.
final
GridDelegate
delegate
;
RenderGrid
createRenderObject
()
=>
new
RenderGrid
(
maxChildExtent:
maxChildExtent
);
GridDelegate
createDelegate
()
=>
delegate
;
}
/// Uses a grid layout with a fixed column count.
///
/// For details about the grid layout algorithm, see [MaxTileWidthGridDelegate].
class
FixedColumnCountGrid
extends
GridRenderObjectWidgetBase
{
FixedColumnCountGrid
(
List
<
Widget
>
children
,
{
Key
key
,
this
.
columnCount
,
this
.
tileAspectRatio
:
1.0
,
this
.
padding
:
EdgeDims
.
zero
})
:
super
(
key:
key
,
children:
children
)
{
assert
(
columnCount
!=
null
);
}
/// The number of columns in the grid.
final
int
columnCount
;
void
updateRenderObject
(
RenderGrid
renderObject
,
Grid
oldWidget
)
{
renderObject
.
maxChildExtent
=
maxChildExtent
;
/// The ratio of the width to the height of each tile in the grid.
final
double
tileAspectRatio
;
/// The amount of padding to apply to each child.
final
EdgeDims
padding
;
FixedColumnCountGridDelegate
createDelegate
()
{
return
new
FixedColumnCountGridDelegate
(
columnCount:
columnCount
,
tileAspectRatio:
tileAspectRatio
,
padding:
padding
);
}
}
/// Uses a grid layout with a max tile width.
///
/// For details about the grid layout algorithm, see [MaxTileWidthGridDelegate].
class
MaxTileWidthGrid
extends
GridRenderObjectWidgetBase
{
MaxTileWidthGrid
(
List
<
Widget
>
children
,
{
Key
key
,
this
.
maxTileWidth
,
this
.
tileAspectRatio
:
1.0
,
this
.
padding
:
EdgeDims
.
zero
})
:
super
(
key:
key
,
children:
children
)
{
assert
(
maxTileWidth
!=
null
);
}
/// The maximum width of a tile in the grid.
final
double
maxTileWidth
;
/// The ratio of the width to the height of each tile in the grid.
final
double
tileAspectRatio
;
/// The amount of padding to apply to each child.
final
EdgeDims
padding
;
MaxTileWidthGridDelegate
createDelegate
()
{
return
new
MaxTileWidthGridDelegate
(
maxTileWidth:
maxTileWidth
,
tileAspectRatio:
tileAspectRatio
,
padding:
padding
);
}
}
/// Supplies per-child data to the grid's [GridDelegate].
class
GridPlacementData
<
DataType
,
WidgetType
extends
RenderObjectWidget
>
extends
ParentDataWidget
<
WidgetType
>
{
GridPlacementData
({
Key
key
,
this
.
placementData
,
Widget
child
})
:
super
(
key:
key
,
child:
child
);
/// Opaque data passed to the getChildPlacement method of the grid's [GridDelegate].
final
DataType
placementData
;
void
applyParentData
(
RenderObject
renderObject
)
{
assert
(
renderObject
.
parentData
is
GridParentData
);
final
GridParentData
parentData
=
renderObject
.
parentData
;
if
(
parentData
.
placementData
!=
placementData
)
{
parentData
.
placementData
=
placementData
;
AbstractNode
targetParent
=
renderObject
.
parent
;
if
(
targetParent
is
RenderObject
)
targetParent
.
markNeedsLayout
();
}
}
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'placementData:
$placementData
'
);
}
}
...
...
packages/flutter/test/rendering/grid_test.dart
View file @
7bf3cb41
...
...
@@ -16,7 +16,10 @@ void main() {
new
RenderDecoratedBox
(
decoration:
new
BoxDecoration
())
];
RenderGrid
grid
=
new
RenderGrid
(
children:
children
,
maxChildExtent:
100.0
);
RenderGrid
grid
=
new
RenderGrid
(
children:
children
,
delegate:
new
MaxTileWidthGridDelegate
(
maxTileWidth:
100.0
)
);
layout
(
grid
,
constraints:
const
BoxConstraints
(
maxWidth:
200.0
));
children
.
forEach
((
RenderBox
child
)
{
...
...
@@ -28,7 +31,7 @@ void main() {
expect
(
grid
.
size
.
height
,
equals
(
200.0
),
reason:
"grid height"
);
expect
(
grid
.
needsLayout
,
equals
(
false
));
grid
.
maxChildExtent
=
60.0
;
grid
.
delegate
=
new
MaxTileWidthGridDelegate
(
maxTileWidth:
60.0
)
;
expect
(
grid
.
needsLayout
,
equals
(
true
));
pumpFrame
();
...
...
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