Commit 6e530ea8 authored by Adam Barth's avatar Adam Barth

Make pesto demo scroll fast (#4443)

We can viewport the content using a ScrollableGrid. Also, we can use the
padding mechanisms of the grid to avoid extra padding widgets.
parent 440aa5ee
...@@ -69,22 +69,19 @@ class _PestoDemoState extends State<PestoDemo> { ...@@ -69,22 +69,19 @@ class _PestoDemoState extends State<PestoDemo> {
child: new Icon(icon: Icons.edit), child: new Icon(icon: Icons.edit),
onPressed: () { } onPressed: () { }
), ),
body: new Block( body: new ScrollableGrid(
padding: new EdgeInsets.only(top: _kAppBarHeight + statusBarHeight), delegate: new MaxTileWidthGridDelegate(
children: <Widget>[ maxTileWidth: 500.0,
new Padding( rowSpacing: 8.0,
padding: const EdgeInsets.all(8.0), columnSpacing: 8.0,
child: new MaxTileWidthGrid( padding: new EdgeInsets.fromLTRB(8.0, 8.0 + _kAppBarHeight + statusBarHeight, 8.0, 8.0)
maxTileWidth: 500.0, ),
children: recipes.map( children: recipes.map(
(Recipe recipe) => new _RecipeCard( (Recipe recipe) => new _RecipeCard(
recipe: recipe, recipe: recipe,
onTap: () { _showRecipe(context, recipe); } onTap: () { _showRecipe(context, recipe); }
)
).toList()
)
) )
] )
) )
) )
); );
...@@ -200,42 +197,39 @@ class _RecipeCard extends StatelessWidget { ...@@ -200,42 +197,39 @@ class _RecipeCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Padding( return new GestureDetector(
padding: const EdgeInsets.all(4.0), onTap: onTap,
child: new GestureDetector( child: new Card(
onTap: onTap, child: new Column(
child: new Card( crossAxisAlignment: CrossAxisAlignment.start,
child: new Column( children: <Widget>[
crossAxisAlignment: CrossAxisAlignment.start, new AssetImage(
children: <Widget>[ name: recipe.imagePath,
new AssetImage( fit: ImageFit.scaleDown
name: recipe.imagePath, ),
fit: ImageFit.scaleDown new Flexible(
), child: new Row(
new Flexible( children: <Widget>[
child: new Row( new Padding(
children: <Widget>[ padding: const EdgeInsets.all(16.0),
new Padding( child: new AssetImage(
padding: const EdgeInsets.all(16.0), width: 48.0,
child: new AssetImage( height: 48.0,
width: 48.0, name: recipe.ingredientsImagePath
height: 48.0,
name: recipe.ingredientsImagePath
)
),
new Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(recipe.name, style: titleStyle),
new Text(recipe.author, style: authorStyle),
]
) )
] ),
) new Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(recipe.name, style: titleStyle),
new Text(recipe.author, style: authorStyle),
]
)
]
) )
] )
) ]
) )
) )
); );
......
...@@ -27,11 +27,11 @@ bool _debugIsMonotonic(List<double> offsets) { ...@@ -27,11 +27,11 @@ bool _debugIsMonotonic(List<double> offsets) {
return result; return result;
} }
List<double> _generateRegularOffsets(int count, double size) { List<double> _generateRegularOffsets(int count, double extent) {
final int length = count + 1; final int length = count + 1;
final List<double> result = new Float64List(length); final List<double> result = new Float64List(length);
for (int i = 0; i < length; ++i) for (int i = 0; i < length; ++i)
result[i] = i * size; result[i] = i * extent;
return result; return result;
} }
...@@ -62,20 +62,26 @@ class GridSpecification { ...@@ -62,20 +62,26 @@ class GridSpecification {
} }
/// Creates a grid specification containing a certain number of equally sized tiles. /// Creates a grid specification containing a certain number of equally sized tiles.
/// The tileWidth is the sum of the width of the child it will contain and ///
/// columnSpacing (even if columnCount is 1). Similarly tileHeight is child's height /// The `tileWidth` and `tileHeight` is the horizontal and vertical
/// plus rowSpacing. If the tiles are to completely fill the grid, then their size /// (respectively) extent that each child will be allocated in the grid. The
/// should be based on the grid's padded interior. /// tiles will have [columnSpacing] space between them horizontally and
/// [rowSpacing] space between them vertically.
///
/// If the tiles are to completely fill the grid, then their size should be
/// based on the grid's padded interior and the column and row spacing.
GridSpecification.fromRegularTiles({ GridSpecification.fromRegularTiles({
double tileWidth, double tileWidth,
double tileHeight, double tileHeight,
int columnCount, int columnCount,
int rowCount, int rowCount,
this.rowSpacing: 0.0, double columnSpacing: 0.0,
this.columnSpacing: 0.0, double rowSpacing: 0.0,
this.padding: EdgeInsets.zero this.padding: EdgeInsets.zero
}) : columnOffsets = _generateRegularOffsets(columnCount, tileWidth), }) : columnOffsets = _generateRegularOffsets(columnCount, tileWidth + columnSpacing),
rowOffsets = _generateRegularOffsets(rowCount, tileHeight) { rowOffsets = _generateRegularOffsets(rowCount, tileHeight + rowSpacing),
columnSpacing = columnSpacing,
rowSpacing = rowSpacing {
assert(_debugIsMonotonic(columnOffsets)); assert(_debugIsMonotonic(columnOffsets));
assert(_debugIsMonotonic(rowOffsets)); assert(_debugIsMonotonic(rowOffsets));
assert(columnSpacing != null && columnSpacing >= 0.0); assert(columnSpacing != null && columnSpacing >= 0.0);
...@@ -85,10 +91,14 @@ class GridSpecification { ...@@ -85,10 +91,14 @@ class GridSpecification {
/// The offsets of the column boundaries in the grid. /// The offsets of the column boundaries in the grid.
/// ///
/// The first offset is the offset of the left edge of the left-most column /// The first offset is the offset of the left edge of the left-most tile
/// from the left edge of the interior of the grid's padding (0.0 if the padding /// from the left edge of the interior of the grid's padding (0.0 if the padding
/// is EdgeOffsets.zero). The last offset is the offset of the right edge of /// is EdgeOffsets.zero). The difference between successive entries is the
/// the right-most column from the left edge of the interior of the grid's padding. /// tile width plus the column spacing.
///
/// The last offset is the offset of the right edge of the right-most tile
/// from the left edge of the interior of the grid's padding (less the
/// [columnSpacing]).
/// ///
/// If there are n columns in the grid, there should be n + 1 entries in this /// If there are n columns in the grid, there should be n + 1 entries in this
/// list. The right edge of the last column is defined as columnOffsets(n), i.e. /// list. The right edge of the last column is defined as columnOffsets(n), i.e.
...@@ -97,22 +107,26 @@ class GridSpecification { ...@@ -97,22 +107,26 @@ class GridSpecification {
/// The offsets of the row boundaries in the grid. /// 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 first offset is the offset of the top edge of the top-most tile from
/// the top edge of the interior of the grid's padding (usually if the padding /// the top edge of the interior of the grid's padding (usually if the padding
/// is EdgeOffsets.zero). The last offset is the offset of the bottom edge of /// is EdgeOffsets.zero). The difference between successive entries is the
/// the bottom-most column from the top edge of the interior of the grid's padding. /// tile height plus the row spacing
///
/// The last offset is the offset of the bottom edge of the bottom-most tile
/// from the top edge of the interior of the grid's padding. (less the
/// [rowSpacing])
/// ///
/// If there are n rows in the grid, there should be n + 1 entries in this /// If there are n rows in the grid, there should be n + 1 entries in this
/// list. The bottom edge of the last row is defined as rowOffsets(n), i.e. /// list. The bottom edge of the last row is defined as rowOffsets(n), i.e.
/// the top edge of an extra row. /// the top edge of an extra row.
final List<double> rowOffsets; final List<double> rowOffsets;
/// The vertical distance between rows. /// The horizontal padding between columns.
final double rowSpacing;
/// The horizontal distance between columns.
final double columnSpacing; final double columnSpacing;
/// The vertical padding between rows.
final double rowSpacing;
/// The interior padding of the grid. /// The interior padding of the grid.
/// ///
/// The grid's size encloses the spaced rows and columns and is then inflated /// The grid's size encloses the spaced rows and columns and is then inflated
...@@ -120,7 +134,12 @@ class GridSpecification { ...@@ -120,7 +134,12 @@ class GridSpecification {
final EdgeInsets padding; final EdgeInsets padding;
/// The size of the grid. /// The size of the grid.
Size get gridSize => new Size(columnOffsets.last + padding.horizontal, rowOffsets.last + padding.vertical); Size get gridSize {
return new Size(
columnOffsets.last + padding.horizontal - columnSpacing,
rowOffsets.last + padding.vertical - rowSpacing
);
}
/// The number of columns in this grid. /// The number of columns in this grid.
int get columnCount => columnOffsets.length - 1; int get columnCount => columnOffsets.length - 1;
...@@ -271,10 +290,10 @@ abstract class GridDelegateWithInOrderChildPlacement extends GridDelegate { ...@@ -271,10 +290,10 @@ abstract class GridDelegateWithInOrderChildPlacement extends GridDelegate {
assert(padding != null && padding.isNonNegative); assert(padding != null && padding.isNonNegative);
} }
/// The horizontal distance between columns. /// The horizontal padding between columns.
final double columnSpacing; final double columnSpacing;
/// The vertical distance between rows. /// The vertical padding between rows.
final double rowSpacing; final double rowSpacing;
/// Insets for the entire grid. /// Insets for the entire grid.
...@@ -327,9 +346,11 @@ class FixedColumnCountGridDelegate extends GridDelegateWithInOrderChildPlacement ...@@ -327,9 +346,11 @@ class FixedColumnCountGridDelegate extends GridDelegateWithInOrderChildPlacement
@override @override
GridSpecification getGridSpecification(BoxConstraints constraints, int childCount) { GridSpecification getGridSpecification(BoxConstraints constraints, int childCount) {
assert(constraints.maxWidth < double.INFINITY); assert(constraints.maxWidth < double.INFINITY);
int rowCount = (childCount / columnCount).ceil(); final int rowCount = (childCount / columnCount).ceil();
double tileWidth = math.max(0.0, constraints.maxWidth - padding.horizontal + columnSpacing) / columnCount; final double interiorWidth = constraints.maxWidth - padding.horizontal;
double tileHeight = tileWidth / tileAspectRatio; final double columnWidth = interiorWidth / columnCount;
final double tileWidth = math.max(0.0, columnWidth - columnSpacing);
final double tileHeight = tileWidth / tileAspectRatio;
return new GridSpecification.fromRegularTiles( return new GridSpecification.fromRegularTiles(
tileWidth: tileWidth, tileWidth: tileWidth,
tileHeight: tileHeight, tileHeight: tileHeight,
...@@ -416,10 +437,17 @@ class MaxTileWidthGridDelegate extends GridDelegateWithInOrderChildPlacement { ...@@ -416,10 +437,17 @@ class MaxTileWidthGridDelegate extends GridDelegateWithInOrderChildPlacement {
); );
} }
final double gridWidth = math.max(0.0, constraints.maxWidth - padding.horizontal); final double gridWidth = math.max(0.0, constraints.maxWidth - padding.horizontal);
int columnCount = (gridWidth / maxTileWidth).ceil(); // We inflate the gridWidth by columnSpacing because the columnSpacing for
int rowCount = (childCount / columnCount).ceil(); // the rightmost tile in the grid doesn't actually consume space in the
double tileWidth = gridWidth / columnCount; // grid because the rightmost tile is flush to the right interior edge of
double tileHeight = tileWidth / tileAspectRatio; // the grid.
final double totalColumnExtent = gridWidth + columnSpacing;
final double maxColumnWidth = maxTileWidth + columnSpacing;
final int columnCount = (totalColumnExtent / maxColumnWidth).ceil();
final int rowCount = (childCount / columnCount).ceil();
final double columnWidth = totalColumnExtent / columnCount;
final double tileWidth = columnWidth - columnSpacing;
final double tileHeight = tileWidth / tileAspectRatio;
return new GridSpecification.fromRegularTiles( return new GridSpecification.fromRegularTiles(
tileWidth: tileWidth, tileWidth: tileWidth,
tileHeight: tileHeight, tileHeight: tileHeight,
...@@ -618,13 +646,13 @@ class RenderGrid extends RenderVirtualViewport<GridParentData> { ...@@ -618,13 +646,13 @@ class RenderGrid extends RenderVirtualViewport<GridParentData> {
assert(placement.column + placement.columnSpan < _specification.columnOffsets.length); assert(placement.column + placement.columnSpan < _specification.columnOffsets.length);
assert(placement.row + placement.rowSpan < _specification.rowOffsets.length); assert(placement.row + placement.rowSpan < _specification.rowOffsets.length);
double tileLeft = gridLeftPadding + _specification.columnOffsets[placement.column]; final double tileLeft = gridLeftPadding + _specification.columnOffsets[placement.column];
double tileRight = gridLeftPadding + _specification.columnOffsets[placement.column + placement.columnSpan]; final double tileRight = gridLeftPadding + _specification.columnOffsets[placement.column + placement.columnSpan] - _specification.columnSpacing;
double tileTop = gridTopPadding + _specification.rowOffsets[placement.row]; final double tileTop = gridTopPadding + _specification.rowOffsets[placement.row];
double tileBottom = gridTopPadding + _specification.rowOffsets[placement.row + placement.rowSpan]; final double tileBottom = gridTopPadding + _specification.rowOffsets[placement.row + placement.rowSpan] - _specification.rowSpacing;
double childWidth = math.max(0.0, tileRight - tileLeft - _specification.columnSpacing); final double childWidth = math.max(0.0, tileRight - tileLeft);
double childHeight = math.max(0.0, tileBottom - tileTop - _specification.rowSpacing); final double childHeight = math.max(0.0, tileBottom - tileTop);
child.layout(new BoxConstraints( child.layout(new BoxConstraints(
minWidth: childWidth, minWidth: childWidth,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment