mirror of
https://github.com/flutter/packages.git
synced 2025-07-02 08:34:31 +08:00
This reverts commit a2e65e9656a018692be2adc4f3e2d689b82adea5.
This commit is contained in:
@ -13,138 +13,25 @@ and the Flutter guide for
|
|||||||
[developing packages and plugins](https://flutter.dev/developing-packages).
|
[developing packages and plugins](https://flutter.dev/developing-packages).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- TODO(DavBot02): Put a short description of the package here that helps potential users
|
TODO: Put a short description of the package here that helps potential users
|
||||||
know whether this package might be useful for them.-->
|
know whether this package might be useful for them.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
This package provides support for multi sized tiles and different layouts.
|
|
||||||
Currently the layouts that are implemented in this package are `Stagger` and
|
|
||||||
`Wrap`.
|
|
||||||
|
|
||||||
The following are some demos of how each of the grids look.
|
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||||
|
|
||||||
A stagger grid demo:
|
|
||||||
|
|
||||||
<!-- TODO(snat-s): Add stagger video demo -->
|
|
||||||
|
|
||||||
A wrap demo:
|
|
||||||
|
|
||||||
<!-- TODO(snat-s): Add wrap video demo -->
|
|
||||||
|
|
||||||
### Stagger Features
|
|
||||||
|
|
||||||
`DynamicGridView` is a subclass of `GridView` and gives access
|
|
||||||
to the `SliverGridDelegate`s that are already implemented in the Flutter
|
|
||||||
Framework. Some `SliverGridDelegate`s are `SliverGridDelegateWithMaxCrossAxisExtent` and
|
|
||||||
`SliverGridDelegateWithFixedCrossAxisCount`. This layout can be used with
|
|
||||||
`DynamicGridView.stagger`.
|
|
||||||
|
|
||||||
### Wrap Features
|
|
||||||
|
|
||||||
The Wrap layout is able to do runs of different widgets and adapt accordingly with
|
|
||||||
the sizes of the children. It can leave spacing with `mainAxisSpacing` and
|
|
||||||
`crossAxisSpacing`.
|
|
||||||
|
|
||||||
Having different sizes in only one of the axis is possible by
|
|
||||||
changing the values of `childCrossAxisExtent` and `childMainAxisExtent`. These
|
|
||||||
values by default are set to have loose constraints, but by giving `childCrossAxisExtent` a specific value like
|
|
||||||
100 pixels, it will make all of the children 100 pixels in the main axis.
|
|
||||||
This layout can be used with `DynamicGridView.wrap` and with
|
|
||||||
`DynamicGridView.builder` and `SliverGridDelegateWithWrapping` as the delegate.
|
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
<!-- TODO(DavBot02): List prerequisites and provide or point to information on how to start using the package. -->
|
TODO: List prerequisites and provide or point to information on how to
|
||||||
|
start using the package.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Use `DynamicGridView`s to access this layouts.
|
TODO: Include short and useful examples for package users. Add longer examples
|
||||||
`DynamicGridView` has some constructors that use `SliverChildListDelegate` like
|
to `/example` folder.
|
||||||
`.wrap` and `.stagger`. For a more efficient option that uses `SliverChildBuilderDelegate` use
|
|
||||||
`.builder`, it works the same as `GridView.builder`.
|
|
||||||
|
|
||||||
### Wrap
|
|
||||||
|
|
||||||
The following are simple examples of how to use `DynamicGridView.wrap`.
|
|
||||||
|
|
||||||
<?code-excerpt "dynamic_grid_view_wrap.dart" (Example)?>
|
|
||||||
```dart
|
|
||||||
final List<Widget> children = List.generate(
|
|
||||||
250,
|
|
||||||
(index) => Container(
|
|
||||||
height: index.isEven ? 100 : 50,
|
|
||||||
width: index.isEven ? 95 : 180,
|
|
||||||
color: index.isEven ? Colors.red : Colors.blue,
|
|
||||||
child: Center(child: Text('Item $index')),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
DynamicGridView.wrap(
|
|
||||||
mainAxisSpacing: 10,
|
|
||||||
crossAxisSpacing: 20,
|
|
||||||
children: children,
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
The following example uses `DynamicGridView.builder` with
|
|
||||||
`SliverGridDelegateWithWrapping`.
|
|
||||||
|
|
||||||
<?code-excerpt "dynamic_grid_view_builder.dart (Example)"?>
|
|
||||||
```dart
|
|
||||||
DynamicGridView.builder(
|
|
||||||
gridDelegate: const SliverGridDelegateWithWrapping(
|
|
||||||
mainAxisSpacing: 20,
|
|
||||||
childMainAxisExtent: 250,
|
|
||||||
childCrossAxisExtent: 50,
|
|
||||||
),
|
|
||||||
itemBuilder: (BuildContext context, int index) {
|
|
||||||
return Container(
|
|
||||||
height: 200,
|
|
||||||
color: index.isEven ? Colors.amber : Colors.blue,
|
|
||||||
child: Center(
|
|
||||||
child: Text('$index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
```
|
|
||||||
|
|
||||||
By using `childCrossAxisExtent` and `childMainAxisExtent` the main axis
|
|
||||||
can be limited to have a specific size and the other can be set to loose
|
|
||||||
constraints.
|
|
||||||
|
|
||||||
<?code-excerpt "wrapping_fixed_axis.dart" (Example)?>
|
|
||||||
```dart
|
|
||||||
DynamicGridView.builder(
|
|
||||||
gridDelegate: const SliverGridDelegateWithWrapping(
|
|
||||||
mainAxisSpacing: 20,
|
|
||||||
childMainAxisExtent: 250,
|
|
||||||
),
|
|
||||||
itemBuilder: (BuildContext context, int index) {
|
|
||||||
return Container(
|
|
||||||
height: 200,
|
|
||||||
color: index.isEven ? Colors.amber : Colors.blue,
|
|
||||||
child: Center(
|
|
||||||
child: Text('$index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stagger
|
|
||||||
|
|
||||||
The `Stagger` layout can be used with the constructor
|
|
||||||
`DynamicGridView.stagger` and still use the delegates from `GridView`
|
|
||||||
like `SliverGridDelegateWithMaxCrossAxisExtent` and
|
|
||||||
`SliverGridDelegateWithFixedCrossAxisCount`.
|
|
||||||
|
|
||||||
<!-- TODO(DavBot02): Add a code example of DynamicGrid.stagger -->
|
|
||||||
|
|
||||||
<!-- TODO(snat-s): Add a video of DynamicGrid.stagger -->
|
|
||||||
|
|
||||||
## Additional information
|
## Additional information
|
||||||
|
|
||||||
<!-- TODO(DavBot02): Tell users more about the package: where to find more information, how to
|
TODO: Tell users more about the package: where to find more information, how to
|
||||||
contribute to the package, how to file issues, what response they can expect
|
contribute to the package, how to file issues, what response they can expect
|
||||||
from the package authors, and more. -->
|
from the package authors, and more.
|
||||||
|
@ -5,4 +5,3 @@
|
|||||||
export 'src/base_grid_layout.dart';
|
export 'src/base_grid_layout.dart';
|
||||||
export 'src/dynamic_grid.dart';
|
export 'src/dynamic_grid.dart';
|
||||||
export 'src/render_dynamic_grid.dart';
|
export 'src/render_dynamic_grid.dart';
|
||||||
export 'src/wrap_layout.dart';
|
|
||||||
|
@ -37,14 +37,14 @@ class DynamicSliverGridGeometry extends SliverGridGeometry {
|
|||||||
crossAxisExtent.isInfinite ? 0.0 : crossAxisExtent;
|
crossAxisExtent.isInfinite ? 0.0 : crossAxisExtent;
|
||||||
|
|
||||||
switch (constraints.axis) {
|
switch (constraints.axis) {
|
||||||
case Axis.vertical:
|
case Axis.horizontal:
|
||||||
return BoxConstraints(
|
return BoxConstraints(
|
||||||
minHeight: mainMinExtent,
|
minHeight: mainMinExtent,
|
||||||
maxHeight: mainAxisExtent,
|
maxHeight: mainAxisExtent,
|
||||||
minWidth: crossMinExtent,
|
minWidth: crossMinExtent,
|
||||||
maxWidth: crossAxisExtent,
|
maxWidth: crossAxisExtent,
|
||||||
);
|
);
|
||||||
case Axis.horizontal:
|
case Axis.vertical:
|
||||||
return BoxConstraints(
|
return BoxConstraints(
|
||||||
minHeight: crossMinExtent,
|
minHeight: crossMinExtent,
|
||||||
maxHeight: crossAxisExtent,
|
maxHeight: crossAxisExtent,
|
||||||
@ -68,17 +68,14 @@ abstract class DynamicSliverGridLayout extends SliverGridLayout {
|
|||||||
/// provide looser constraints to the child, whose size after layout can be
|
/// provide looser constraints to the child, whose size after layout can be
|
||||||
/// reported back to the layout object in [updateGeometryForChildIndex].
|
/// reported back to the layout object in [updateGeometryForChildIndex].
|
||||||
@override
|
@override
|
||||||
DynamicSliverGridGeometry getGeometryForChildIndex(int index);
|
SliverGridGeometry getGeometryForChildIndex(int index);
|
||||||
|
|
||||||
/// Update the size and position of the child with the given index,
|
/// Update the size and position of the child with the given index,
|
||||||
/// considering the size of the child after layout.
|
/// considering the size of the child after layout.
|
||||||
///
|
///
|
||||||
/// This is used to update the layout object after the child has laid out,
|
/// This is used to update the layout object after the child has laid out,
|
||||||
/// allowing the layout pattern to adapt to the child's size.
|
/// allowing the layout pattern to adapt to the child's size.
|
||||||
DynamicSliverGridGeometry updateGeometryForChildIndex(
|
SliverGridGeometry updateGeometryForChildIndex(int index, Size childSize);
|
||||||
int index,
|
|
||||||
Size childSize,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Called by [RenderDynamicSliverGrid] to validate the layout pattern has
|
/// Called by [RenderDynamicSliverGrid] to validate the layout pattern has
|
||||||
/// filled the screen.
|
/// filled the screen.
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'render_dynamic_grid.dart';
|
import 'render_dynamic_grid.dart';
|
||||||
import 'wrap_layout.dart';
|
|
||||||
|
|
||||||
/// A scrollable, 2D array of widgets.
|
/// A scrollable, 2D array of widgets.
|
||||||
///
|
///
|
||||||
@ -13,10 +12,13 @@ import 'wrap_layout.dart';
|
|||||||
class DynamicGridView extends GridView {
|
class DynamicGridView extends GridView {
|
||||||
/// Creates a scrollable, 2D array of widgets with a custom
|
/// Creates a scrollable, 2D array of widgets with a custom
|
||||||
/// [SliverGridDelegate].
|
/// [SliverGridDelegate].
|
||||||
|
///
|
||||||
|
// TODO(all): what other parameters should we add to these
|
||||||
|
// constructors, here, builder, etc.?
|
||||||
|
// + reverse
|
||||||
|
// + scrollDirection
|
||||||
DynamicGridView({
|
DynamicGridView({
|
||||||
super.key,
|
super.key,
|
||||||
super.scrollDirection,
|
|
||||||
super.reverse,
|
|
||||||
required super.gridDelegate,
|
required super.gridDelegate,
|
||||||
// This creates a SliverChildListDelegate in the super class.
|
// This creates a SliverChildListDelegate in the super class.
|
||||||
super.children = const <Widget>[],
|
super.children = const <Widget>[],
|
||||||
@ -25,77 +27,13 @@ class DynamicGridView extends GridView {
|
|||||||
/// Creates a scrollable, 2D array of widgets that are created on demand.
|
/// Creates a scrollable, 2D array of widgets that are created on demand.
|
||||||
DynamicGridView.builder({
|
DynamicGridView.builder({
|
||||||
super.key,
|
super.key,
|
||||||
super.scrollDirection,
|
|
||||||
super.reverse,
|
|
||||||
required super.gridDelegate,
|
required super.gridDelegate,
|
||||||
// This creates a SliverChildBuilderDelegate in the super class.
|
// This creates a SliverChildBuilderDelegate in the super class.
|
||||||
required super.itemBuilder,
|
required super.itemBuilder,
|
||||||
super.itemCount,
|
super.itemCount,
|
||||||
}) : super.builder();
|
}) : super.builder();
|
||||||
|
|
||||||
/// Creates a scrollable, 2D array of widgets with tiles where each tile can
|
// TODO(snat-s): DynamicGridView.wrap?
|
||||||
/// have its own size.
|
|
||||||
///
|
|
||||||
/// Uses a [SliverGridDelegateWithWrapping] as the [gridDelegate].
|
|
||||||
///
|
|
||||||
/// The following example shows how to use the DynamicGridView.wrap constructor.
|
|
||||||
///
|
|
||||||
/// ```dart
|
|
||||||
/// DynamicGridView.wrap(
|
|
||||||
/// mainAxisSpacing: 10,
|
|
||||||
/// crossAxisSpacing: 20,
|
|
||||||
/// children: [
|
|
||||||
/// Container(
|
|
||||||
/// height: 100,
|
|
||||||
/// width: 200,
|
|
||||||
/// color: Colors.amberAccent[100],
|
|
||||||
/// child: const Center(child: Text('Item 1')
|
|
||||||
/// ),
|
|
||||||
/// ),
|
|
||||||
/// Container(
|
|
||||||
/// height: 50,
|
|
||||||
/// width: 70,
|
|
||||||
/// color: Colors.blue[100],
|
|
||||||
/// child: const Center(child: Text('Item 2'),
|
|
||||||
/// ),
|
|
||||||
/// ),
|
|
||||||
/// Container(
|
|
||||||
/// height: 82,
|
|
||||||
/// width: 300,
|
|
||||||
/// color: Colors.pink[100],
|
|
||||||
/// child: const Center(child: Text('Item 3'),
|
|
||||||
/// ),
|
|
||||||
/// ),
|
|
||||||
/// Container(
|
|
||||||
/// color: Colors.green[100],
|
|
||||||
/// child: const Center(child: Text('Item 3'),
|
|
||||||
/// ),
|
|
||||||
/// ),
|
|
||||||
/// ],
|
|
||||||
/// ),
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// See also:
|
|
||||||
///
|
|
||||||
/// * [SliverGridDelegateWithWrapping] to see a more detailed explanation of
|
|
||||||
/// how the wrapping works.
|
|
||||||
DynamicGridView.wrap({
|
|
||||||
super.key,
|
|
||||||
super.scrollDirection,
|
|
||||||
super.reverse,
|
|
||||||
double mainAxisSpacing = 0.0,
|
|
||||||
double crossAxisSpacing = 0.0,
|
|
||||||
double childCrossAxisExtent = double.infinity,
|
|
||||||
double childMainAxisExtent = double.infinity,
|
|
||||||
super.children = const <Widget>[],
|
|
||||||
}) : super(
|
|
||||||
gridDelegate: SliverGridDelegateWithWrapping(
|
|
||||||
mainAxisSpacing: mainAxisSpacing,
|
|
||||||
crossAxisSpacing: crossAxisSpacing,
|
|
||||||
childCrossAxisExtent: childCrossAxisExtent,
|
|
||||||
childMainAxisExtent: childMainAxisExtent,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO(DavBot09): DynamicGridView.stagger?
|
// TODO(DavBot09): DynamicGridView.stagger?
|
||||||
|
|
||||||
|
@ -1,268 +0,0 @@
|
|||||||
// Copyright 2013 The Flutter 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/rendering.dart';
|
|
||||||
|
|
||||||
import 'base_grid_layout.dart';
|
|
||||||
|
|
||||||
// The model that tracks the current max size of the Sliver in the mainAxis and
|
|
||||||
// tracks if there is still space on the crossAxis.
|
|
||||||
class _RunMetrics {
|
|
||||||
_RunMetrics({
|
|
||||||
required this.maxSliver,
|
|
||||||
required this.currentSizeUsed,
|
|
||||||
required this.scrollOffset,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// The biggest sliver size for the current run.
|
|
||||||
double maxSliver;
|
|
||||||
|
|
||||||
/// The current size that has been used in the current run.
|
|
||||||
double currentSizeUsed;
|
|
||||||
|
|
||||||
/// The scroll offset in the current run.
|
|
||||||
double scrollOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [DynamicSliverGridLayout] that uses dynamically sized tiles.
|
|
||||||
///
|
|
||||||
/// Rather that providing a grid with a [DynamicSliverGridLayout] directly, instead
|
|
||||||
/// provide the grid a [SliverGridDelegate], which can compute a
|
|
||||||
/// [DynamicSliverGridLayout] given the current [SliverConstraints].
|
|
||||||
///
|
|
||||||
/// This layout is used by [SliverGridDelegateWithWrapping].
|
|
||||||
///
|
|
||||||
/// See also:
|
|
||||||
///
|
|
||||||
/// * [SliverGridDelegateWithWrapping], which uses this layout.
|
|
||||||
/// * [DynamicSliverGridLayout], which represents an arbitrary dynamic tile layout.
|
|
||||||
/// * [DynamicSliverGridGeometry], which represents the size and position of a
|
|
||||||
/// single tile in a grid.
|
|
||||||
/// * [SliverGridDelegate.getLayout], which returns this object to describe the
|
|
||||||
/// delegate's layout.
|
|
||||||
/// * [RenderDynamicSliverGrid], which uses this class during its
|
|
||||||
/// [RenderDynamicSliverGrid.performLayout] method.
|
|
||||||
class SliverGridWrappingTileLayout extends DynamicSliverGridLayout {
|
|
||||||
/// Creates a layout that uses dynamic sized and spaced tiles.
|
|
||||||
///
|
|
||||||
/// All of the arguments must not be null and must not be negative.
|
|
||||||
SliverGridWrappingTileLayout({
|
|
||||||
required this.mainAxisSpacing,
|
|
||||||
required this.crossAxisSpacing,
|
|
||||||
required this.childMainAxisExtent,
|
|
||||||
required this.childCrossAxisExtent,
|
|
||||||
required this.crossAxisExtent,
|
|
||||||
required this.scrollDirection,
|
|
||||||
}) : assert(mainAxisSpacing != null && mainAxisSpacing >= 0),
|
|
||||||
assert(crossAxisSpacing != null && crossAxisSpacing >= 0),
|
|
||||||
assert(childMainAxisExtent != null && childMainAxisExtent >= 0),
|
|
||||||
assert(childCrossAxisExtent != null && childCrossAxisExtent >= 0),
|
|
||||||
assert(crossAxisExtent != null && crossAxisExtent >= 0),
|
|
||||||
assert(scrollDirection != null &&
|
|
||||||
(scrollDirection == Axis.horizontal ||
|
|
||||||
scrollDirection == Axis.vertical));
|
|
||||||
|
|
||||||
/// The direction in wich the layout should be built.
|
|
||||||
final Axis scrollDirection;
|
|
||||||
|
|
||||||
/// The extent of the child in the non-scrolling axis.
|
|
||||||
final double crossAxisExtent;
|
|
||||||
|
|
||||||
/// The number of logical pixels between each child along the main axis.
|
|
||||||
final double mainAxisSpacing;
|
|
||||||
|
|
||||||
/// The number of logical pixels between each child along the cross axis.
|
|
||||||
final double crossAxisSpacing;
|
|
||||||
|
|
||||||
/// The number of pixels from the leading edge of one tile to the trailing
|
|
||||||
/// edge of the same tile in the main axis.
|
|
||||||
final double childMainAxisExtent;
|
|
||||||
|
|
||||||
/// The number of pixels from the leading edge of one tile to the trailing
|
|
||||||
/// edge of the same tile in the cross axis.
|
|
||||||
final double childCrossAxisExtent;
|
|
||||||
|
|
||||||
/// The model that is used internally to keep track of how much space is left
|
|
||||||
/// and how much has been used.
|
|
||||||
final List<_RunMetrics> _model = <_RunMetrics>[
|
|
||||||
_RunMetrics(maxSliver: 0.0, currentSizeUsed: 0.0, scrollOffset: 0.0)
|
|
||||||
];
|
|
||||||
|
|
||||||
// This method provides the initial constraints for the child to layout,
|
|
||||||
// and then it is updated with the final size later in
|
|
||||||
// updateGeometryForChildIndex.
|
|
||||||
@override
|
|
||||||
DynamicSliverGridGeometry getGeometryForChildIndex(int index) {
|
|
||||||
return DynamicSliverGridGeometry(
|
|
||||||
scrollOffset: 0,
|
|
||||||
crossAxisOffset: 0,
|
|
||||||
mainAxisExtent: childMainAxisExtent,
|
|
||||||
crossAxisExtent: childCrossAxisExtent,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
DynamicSliverGridGeometry updateGeometryForChildIndex(
|
|
||||||
int index,
|
|
||||||
Size childSize,
|
|
||||||
) {
|
|
||||||
final double scrollOffset = _model.last.scrollOffset;
|
|
||||||
final double currentSizeUsed = _model.last.currentSizeUsed;
|
|
||||||
late final double addedSize;
|
|
||||||
|
|
||||||
switch (scrollDirection) {
|
|
||||||
case Axis.vertical:
|
|
||||||
addedSize = currentSizeUsed + childSize.width + crossAxisSpacing;
|
|
||||||
break;
|
|
||||||
case Axis.horizontal:
|
|
||||||
addedSize = currentSizeUsed + childSize.height + mainAxisSpacing;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addedSize > crossAxisExtent && _model.last.currentSizeUsed > 0.0) {
|
|
||||||
switch (scrollDirection) {
|
|
||||||
case Axis.vertical:
|
|
||||||
_model.add(
|
|
||||||
_RunMetrics(
|
|
||||||
maxSliver: childSize.height + mainAxisSpacing,
|
|
||||||
currentSizeUsed: childSize.width + crossAxisSpacing,
|
|
||||||
scrollOffset:
|
|
||||||
scrollOffset + _model.last.maxSliver + mainAxisSpacing,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case Axis.horizontal:
|
|
||||||
_model.add(
|
|
||||||
_RunMetrics(
|
|
||||||
maxSliver: childSize.width + crossAxisSpacing,
|
|
||||||
currentSizeUsed: childSize.height + mainAxisSpacing,
|
|
||||||
scrollOffset:
|
|
||||||
scrollOffset + _model.last.maxSliver + crossAxisSpacing,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DynamicSliverGridGeometry(
|
|
||||||
scrollOffset: _model.last.scrollOffset,
|
|
||||||
crossAxisOffset: 0.0,
|
|
||||||
mainAxisExtent: childSize.height + mainAxisSpacing,
|
|
||||||
crossAxisExtent: childSize.width + crossAxisSpacing,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
_model.last.currentSizeUsed = addedSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (scrollDirection) {
|
|
||||||
case Axis.vertical:
|
|
||||||
if (childSize.height + mainAxisSpacing > _model.last.maxSliver) {
|
|
||||||
_model.last.maxSliver = childSize.height + mainAxisSpacing;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Axis.horizontal:
|
|
||||||
if (childSize.width + crossAxisSpacing > _model.last.maxSliver) {
|
|
||||||
_model.last.maxSliver = childSize.width + crossAxisSpacing;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DynamicSliverGridGeometry(
|
|
||||||
scrollOffset: scrollOffset,
|
|
||||||
crossAxisOffset: currentSizeUsed,
|
|
||||||
mainAxisExtent: childSize.height,
|
|
||||||
crossAxisExtent: childSize.width,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool reachedTargetScrollOffset(double targetOffset) {
|
|
||||||
return _model.last.scrollOffset > targetOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [SliverGridDelegate] for creating grids that wrap variably sized tiles.
|
|
||||||
///
|
|
||||||
/// For example, if the grid is vertical, this delegate will create a layout
|
|
||||||
/// where the children are layed out until they fill the horizontal axis and then
|
|
||||||
/// they continue in the next row. If the grid is horizontal, this delegate will
|
|
||||||
/// do the same but it will fill the vertical axis and will pass to another
|
|
||||||
/// column until it finishes.
|
|
||||||
///
|
|
||||||
/// This delegate creates grids with different sized tiles. Tiles
|
|
||||||
/// can have fixed dimensions if [childCrossAxisExtent] or
|
|
||||||
/// [childMainAxisExtent] are provided.
|
|
||||||
///
|
|
||||||
/// See also:
|
|
||||||
/// * [DynamicGridView.wrap], wich is a constructor to use this [SliverGridDelegate],
|
|
||||||
/// like `GridView.extent`.
|
|
||||||
/// * [DynamicGridView], which can use this delegate to control the layout of its
|
|
||||||
/// tiles.
|
|
||||||
/// * [RenderDynamicSliverGrid], which can use this delegate to control the
|
|
||||||
/// layout of its tiles.
|
|
||||||
class SliverGridDelegateWithWrapping extends SliverGridDelegate {
|
|
||||||
/// Create a delegate that wraps variably sized tiles.
|
|
||||||
///
|
|
||||||
/// The children widgets are provided with loose constraints, and if any of the
|
|
||||||
/// extent parameters are set, the children are given tight constraints.
|
|
||||||
/// The way that children are made to have loose constraints is by assigning
|
|
||||||
/// the value of [double.infinity] to [childMainAxisExtent] and
|
|
||||||
/// [childCrossAxisExtent].
|
|
||||||
/// To have same sized tiles with the wrapping, specify the [childCrossAxisExtent]
|
|
||||||
/// and the [childMainAxisExtent] to be the same size. Or only one of them to
|
|
||||||
/// be of a certain size in one of the axis.
|
|
||||||
const SliverGridDelegateWithWrapping({
|
|
||||||
this.mainAxisSpacing = 0.0,
|
|
||||||
this.crossAxisSpacing = 0.0,
|
|
||||||
this.childCrossAxisExtent = double.infinity,
|
|
||||||
this.childMainAxisExtent = double.infinity,
|
|
||||||
}) : assert(mainAxisSpacing != null && mainAxisSpacing >= 0),
|
|
||||||
assert(crossAxisSpacing != null && crossAxisSpacing >= 0);
|
|
||||||
|
|
||||||
/// The number of pixels from the leading edge of one tile to the trailing
|
|
||||||
/// edge of the same tile in the main axis.
|
|
||||||
///
|
|
||||||
/// Defaults to [double.infinity] to provide the child with loose constraints.
|
|
||||||
final double childMainAxisExtent;
|
|
||||||
|
|
||||||
/// The number of pixels from the leading edge of one tile to the trailing
|
|
||||||
/// edge of the same tile in the cross axis.
|
|
||||||
///
|
|
||||||
/// Defaults to [double.infinity] to provide the child with loose constraints.
|
|
||||||
final double childCrossAxisExtent;
|
|
||||||
|
|
||||||
/// The number of logical pixels between each child along the main axis.
|
|
||||||
///
|
|
||||||
/// Defaults to 0.0
|
|
||||||
final double mainAxisSpacing;
|
|
||||||
|
|
||||||
/// The number of logical pixels between each child along the cross axis.
|
|
||||||
///
|
|
||||||
/// Defaults to 0.0
|
|
||||||
final double crossAxisSpacing;
|
|
||||||
|
|
||||||
bool _debugAssertIsValid() {
|
|
||||||
assert(mainAxisSpacing >= 0.0);
|
|
||||||
assert(crossAxisSpacing >= 0.0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
SliverGridLayout getLayout(SliverConstraints constraints) {
|
|
||||||
assert(_debugAssertIsValid());
|
|
||||||
return SliverGridWrappingTileLayout(
|
|
||||||
childMainAxisExtent: childMainAxisExtent,
|
|
||||||
childCrossAxisExtent: childCrossAxisExtent,
|
|
||||||
mainAxisSpacing: mainAxisSpacing,
|
|
||||||
crossAxisSpacing: crossAxisSpacing,
|
|
||||||
scrollDirection: axisDirectionToAxis(constraints.axisDirection),
|
|
||||||
crossAxisExtent: constraints.crossAxisExtent,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldRelayout(SliverGridDelegateWithWrapping oldDelegate) {
|
|
||||||
return oldDelegate.mainAxisSpacing != mainAxisSpacing ||
|
|
||||||
oldDelegate.crossAxisSpacing != crossAxisSpacing;
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,8 +12,8 @@ void main() {
|
|||||||
const DynamicSliverGridGeometry geometry = DynamicSliverGridGeometry(
|
const DynamicSliverGridGeometry geometry = DynamicSliverGridGeometry(
|
||||||
scrollOffset: 0,
|
scrollOffset: 0,
|
||||||
crossAxisOffset: 0,
|
crossAxisOffset: 0,
|
||||||
crossAxisExtent: 150.0,
|
mainAxisExtent: 150.0,
|
||||||
mainAxisExtent: 50.0,
|
crossAxisExtent: 50.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Vertical
|
// Vertical
|
||||||
|
@ -38,7 +38,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Only the visible tiles have been laid out.
|
// Only the visible tiles have ben laid out.
|
||||||
expect(find.text('Index 0'), findsOneWidget);
|
expect(find.text('Index 0'), findsOneWidget);
|
||||||
expect(tester.getTopLeft(find.text('Index 0')), Offset.zero);
|
expect(tester.getTopLeft(find.text('Index 0')), Offset.zero);
|
||||||
expect(find.text('Index 1'), findsOneWidget);
|
expect(find.text('Index 1'), findsOneWidget);
|
||||||
@ -69,7 +69,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Only the visible tiles have been laid out, up to itemCount.
|
// Only the visible tiles have ben laid out, up to itemCount.
|
||||||
expect(find.text('Index 0'), findsOneWidget);
|
expect(find.text('Index 0'), findsOneWidget);
|
||||||
expect(tester.getTopLeft(find.text('Index 0')), Offset.zero);
|
expect(tester.getTopLeft(find.text('Index 0')), Offset.zero);
|
||||||
expect(find.text('Index 1'), findsOneWidget);
|
expect(find.text('Index 1'), findsOneWidget);
|
||||||
@ -119,9 +119,9 @@ class TestSimpleLayout extends DynamicSliverGridLayout {
|
|||||||
static const double childExtent = 50.0;
|
static const double childExtent = 50.0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DynamicSliverGridGeometry getGeometryForChildIndex(int index) {
|
SliverGridGeometry getGeometryForChildIndex(int index) {
|
||||||
final double crossAxisStart = (index % crossAxisCount) * childExtent;
|
final double crossAxisStart = (index % crossAxisCount) * childExtent;
|
||||||
return DynamicSliverGridGeometry(
|
return SliverGridGeometry(
|
||||||
scrollOffset: (index ~/ crossAxisCount) * childExtent,
|
scrollOffset: (index ~/ crossAxisCount) * childExtent,
|
||||||
crossAxisOffset: crossAxisStart,
|
crossAxisOffset: crossAxisStart,
|
||||||
mainAxisExtent: childExtent,
|
mainAxisExtent: childExtent,
|
||||||
@ -133,10 +133,7 @@ class TestSimpleLayout extends DynamicSliverGridLayout {
|
|||||||
bool reachedTargetScrollOffset(double targetOffset) => true;
|
bool reachedTargetScrollOffset(double targetOffset) => true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DynamicSliverGridGeometry updateGeometryForChildIndex(
|
SliverGridGeometry updateGeometryForChildIndex(int index, Size childSize) {
|
||||||
int index,
|
|
||||||
Size childSize,
|
|
||||||
) {
|
|
||||||
return getGeometryForChildIndex(index);
|
return getGeometryForChildIndex(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,7 +142,7 @@ class TestDelegate extends SliverGridDelegateWithFixedCrossAxisCount {
|
|||||||
TestDelegate({required super.crossAxisCount});
|
TestDelegate({required super.crossAxisCount});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DynamicSliverGridLayout getLayout(SliverConstraints constraints) {
|
SliverGridLayout getLayout(SliverConstraints constraints) {
|
||||||
return TestSimpleLayout(crossAxisCount: crossAxisCount);
|
return TestSimpleLayout(crossAxisCount: crossAxisCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,636 +0,0 @@
|
|||||||
// Copyright 2013 The Flutter 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:dynamic_layouts/dynamic_layouts.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
testWidgets(
|
|
||||||
'DynamicGridView generates children and checks if they are layed out',
|
|
||||||
(WidgetTester tester) async {
|
|
||||||
final List<Widget> children = List<Widget>.generate(
|
|
||||||
10,
|
|
||||||
(int index) => SizedBox(
|
|
||||||
height: index.isEven ? 100 : 50,
|
|
||||||
width: index.isEven ? 95 : 180,
|
|
||||||
child: Text('Item $index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: DynamicGridView(
|
|
||||||
gridDelegate: const SliverGridDelegateWithWrapping(),
|
|
||||||
children: children,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check that the children are in the tree
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
expect(find.text('Item $i'), findsOneWidget);
|
|
||||||
}
|
|
||||||
// Check that the children are in the right position
|
|
||||||
expect(tester.getTopLeft(find.text('Item 0')), const Offset(0.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 1')), const Offset(95.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 2')), const Offset(275.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 3')), const Offset(370.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 4')), const Offset(550.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 5')), const Offset(0.0, 100.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 6')), const Offset(180.0, 100.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 7')), const Offset(275.0, 100.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 8')), const Offset(455.0, 100.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 9')), const Offset(550.0, 100.0));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets(
|
|
||||||
'Test for wrap that generates children and checks if they are layed out',
|
|
||||||
(WidgetTester tester) async {
|
|
||||||
final List<Widget> children = List<Widget>.generate(
|
|
||||||
10,
|
|
||||||
(int index) => SizedBox(
|
|
||||||
height: index.isEven ? 100 : 50,
|
|
||||||
width: index.isEven ? 95 : 180,
|
|
||||||
child: Text('Item $index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: DynamicGridView.wrap(
|
|
||||||
children: children,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
expect(find.text('Item $i'), findsOneWidget);
|
|
||||||
}
|
|
||||||
// Check that the children are in the right position
|
|
||||||
expect(tester.getTopLeft(find.text('Item 0')), const Offset(0.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 1')), const Offset(95.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 2')), const Offset(275.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 3')), const Offset(370.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 4')), const Offset(550.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 5')), const Offset(0.0, 100.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 6')), const Offset(180.0, 100.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 7')), const Offset(275.0, 100.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 8')), const Offset(455.0, 100.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 9')), const Offset(550.0, 100.0));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Test for wrap to be laying child dynamically',
|
|
||||||
(WidgetTester tester) async {
|
|
||||||
final List<Widget> children = List<Widget>.generate(
|
|
||||||
20,
|
|
||||||
(int index) => SizedBox(
|
|
||||||
height: index.isEven ? 1000 : 50,
|
|
||||||
width: index.isEven ? 95 : 180,
|
|
||||||
child: Text('Item $index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: DynamicGridView.builder(
|
|
||||||
itemCount: children.length,
|
|
||||||
gridDelegate: const SliverGridDelegateWithWrapping(),
|
|
||||||
itemBuilder: (BuildContext context, int index) => children[index],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
for (int i = 0; i < 5; i++) {
|
|
||||||
expect(find.text('Item $i'), findsOneWidget);
|
|
||||||
}
|
|
||||||
// Check that the children are in the right position
|
|
||||||
expect(tester.getTopLeft(find.text('Item 0')), const Offset(0.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 1')), const Offset(95.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 2')), const Offset(275.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 3')), const Offset(370.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 4')), const Offset(550.0, 0.0));
|
|
||||||
expect(find.text('Item 5'), findsNothing);
|
|
||||||
await tester.scrollUntilVisible(find.text('Item 19'), 500.0);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(find.text('Item 18'), findsOneWidget);
|
|
||||||
expect(tester.getTopLeft(find.text('Item 18')), const Offset(455.0, 0.0));
|
|
||||||
|
|
||||||
expect(find.text('Item 0'), findsNothing);
|
|
||||||
expect(find.text('Item 1'), findsNothing);
|
|
||||||
expect(find.text('Item 2'), findsNothing);
|
|
||||||
expect(find.text('Item 3'), findsNothing);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets(
|
|
||||||
'Test for DynamicGridView.wrap to scrollDirection Axis.horizontal',
|
|
||||||
(WidgetTester tester) async {
|
|
||||||
final List<Widget> children = List<Widget>.generate(
|
|
||||||
20,
|
|
||||||
(int index) => SizedBox(
|
|
||||||
height: index.isEven ? 100 : 50,
|
|
||||||
width: index.isEven ? 100 : 180,
|
|
||||||
child: Text('Item $index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: DynamicGridView.wrap(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
children: children,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
for (int i = 0; i < 20; i++) {
|
|
||||||
expect(find.text('Item $i'), findsOneWidget);
|
|
||||||
}
|
|
||||||
// Check that the children are in the right position
|
|
||||||
double dy = 0, dx = 0;
|
|
||||||
for (int i = 0; i < 20; i++) {
|
|
||||||
if (dy >= 600.0) {
|
|
||||||
dy = 0.0;
|
|
||||||
dx += 180.0;
|
|
||||||
}
|
|
||||||
expect(tester.getTopLeft(find.text('Item $i')), Offset(dx, dy));
|
|
||||||
dy += i.isEven ? 100 : 50;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Test DynamicGridView.builder for GridView.reverse to true',
|
|
||||||
(WidgetTester tester) async {
|
|
||||||
final List<Widget> children = List<Widget>.generate(
|
|
||||||
10,
|
|
||||||
(int index) => SizedBox(
|
|
||||||
height: index.isEven ? 100 : 50,
|
|
||||||
width: index.isEven ? 100 : 180,
|
|
||||||
child: Text('Item $index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: DynamicGridView.builder(
|
|
||||||
reverse: true,
|
|
||||||
itemCount: children.length,
|
|
||||||
gridDelegate: const SliverGridDelegateWithWrapping(),
|
|
||||||
itemBuilder: (BuildContext context, int index) => children[index],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
expect(find.text('Item $i'), findsOneWidget);
|
|
||||||
}
|
|
||||||
double dx = 0.0, dy = 600.0;
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
if (dx >= 600.0) {
|
|
||||||
dx = 0.0;
|
|
||||||
dy -= 100.0;
|
|
||||||
}
|
|
||||||
expect(tester.getBottomLeft(find.text('Item $i')), Offset(dx, dy));
|
|
||||||
dx += i.isEven ? 100 : 180;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('DynamicGridView.wrap for GridView.reverse to true',
|
|
||||||
(WidgetTester tester) async {
|
|
||||||
final List<Widget> children = List<Widget>.generate(
|
|
||||||
20,
|
|
||||||
(int index) => SizedBox(
|
|
||||||
height: index.isEven ? 100 : 50,
|
|
||||||
width: index.isEven ? 100 : 180,
|
|
||||||
child: Text('Item $index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: DynamicGridView.wrap(
|
|
||||||
reverse: true,
|
|
||||||
children: children,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
for (int i = 0; i < 20; i++) {
|
|
||||||
expect(find.text('Item $i'), findsOneWidget);
|
|
||||||
}
|
|
||||||
// Check that the children are in the right position
|
|
||||||
double dx = 0.0, dy = 600.0;
|
|
||||||
for (int i = 0; i < 20; i++) {
|
|
||||||
if (dx >= 600.0) {
|
|
||||||
dx = 0.0;
|
|
||||||
dy -= 100.0;
|
|
||||||
}
|
|
||||||
expect(tester.getBottomLeft(find.text('Item $i')), Offset(dx, dy));
|
|
||||||
dx += i.isEven ? 100 : 180;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('DynamicGridView.wrap dismiss keyboard onDrag test',
|
|
||||||
(WidgetTester tester) async {
|
|
||||||
final List<FocusNode> focusNodes =
|
|
||||||
List<FocusNode>.generate(50, (int i) => FocusNode());
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
textFieldBoilerplate(
|
|
||||||
child: GridView.extent(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
maxCrossAxisExtent: 300,
|
|
||||||
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
|
|
||||||
children: focusNodes.map((FocusNode focusNode) {
|
|
||||||
return Container(
|
|
||||||
height: 50,
|
|
||||||
color: Colors.green,
|
|
||||||
child: TextField(
|
|
||||||
focusNode: focusNode,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final Finder finder = find.byType(TextField).first;
|
|
||||||
final TextField textField = tester.widget(finder);
|
|
||||||
await tester.showKeyboard(finder);
|
|
||||||
expect(textField.focusNode!.hasFocus, isTrue);
|
|
||||||
|
|
||||||
await tester.drag(finder, const Offset(0.0, -40.0));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
expect(textField.focusNode!.hasFocus, isFalse);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('ChildMainAxisExtent & childCrossAxisExtent are respected',
|
|
||||||
(WidgetTester tester) async {
|
|
||||||
final List<Widget> children = List<Widget>.generate(
|
|
||||||
10,
|
|
||||||
(int index) => SizedBox(
|
|
||||||
key: Key(index.toString()),
|
|
||||||
child: Text('Item $index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: DynamicGridView.builder(
|
|
||||||
gridDelegate: const SliverGridDelegateWithWrapping(
|
|
||||||
childMainAxisExtent: 150,
|
|
||||||
childCrossAxisExtent: 200,
|
|
||||||
),
|
|
||||||
itemCount: children.length,
|
|
||||||
itemBuilder: (BuildContext context, int index) => children[index],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
final Size sizeOfCurrent = tester.getSize(find.byKey(Key('$i')));
|
|
||||||
expect(sizeOfCurrent.width, equals(200));
|
|
||||||
expect(sizeOfCurrent.height, equals(150));
|
|
||||||
}
|
|
||||||
// Check that the children are in the right position
|
|
||||||
double dy = 0, dx = 0;
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
if (dx > 600.0) {
|
|
||||||
dx = 0.0;
|
|
||||||
dy += 150.0;
|
|
||||||
}
|
|
||||||
expect(tester.getTopLeft(find.text('Item $i')), Offset(dx, dy));
|
|
||||||
dx += 200;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('ChildMainAxisExtent is respected', (WidgetTester tester) async {
|
|
||||||
final List<Widget> children = List<Widget>.generate(
|
|
||||||
10,
|
|
||||||
(int index) => SizedBox(
|
|
||||||
key: Key(index.toString()),
|
|
||||||
width: index.isEven ? 100 : 180,
|
|
||||||
child: Text('Item $index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: DynamicGridView.builder(
|
|
||||||
gridDelegate: const SliverGridDelegateWithWrapping(
|
|
||||||
childMainAxisExtent: 200,
|
|
||||||
),
|
|
||||||
itemCount: children.length,
|
|
||||||
itemBuilder: (BuildContext context, int index) => children[index],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
final Size sizeOfCurrent = tester.getSize(find.byKey(Key('$i')));
|
|
||||||
expect(sizeOfCurrent.height, equals(200));
|
|
||||||
}
|
|
||||||
// Check that the children are in the right position
|
|
||||||
double dy = 0, dx = 0;
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
if (dx >= 600.0) {
|
|
||||||
dx = 0.0;
|
|
||||||
dy += 200.0;
|
|
||||||
}
|
|
||||||
expect(tester.getTopLeft(find.text('Item $i')), Offset(dx, dy));
|
|
||||||
dx += i.isEven ? 100 : 180;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('ChildCrossAxisExtent is respected', (WidgetTester tester) async {
|
|
||||||
final List<Widget> children = List<Widget>.generate(
|
|
||||||
10,
|
|
||||||
(int index) => SizedBox(
|
|
||||||
height: index.isEven ? 100 : 50,
|
|
||||||
key: Key(index.toString()),
|
|
||||||
child: Text('Item $index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: DynamicGridView.builder(
|
|
||||||
gridDelegate: const SliverGridDelegateWithWrapping(
|
|
||||||
childCrossAxisExtent: 150,
|
|
||||||
),
|
|
||||||
itemCount: children.length,
|
|
||||||
itemBuilder: (BuildContext context, int index) => children[index],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
final Size sizeOfCurrent = tester.getSize(find.byKey(Key('$i')));
|
|
||||||
expect(sizeOfCurrent.width, equals(150));
|
|
||||||
}
|
|
||||||
// Check that the children are in the right position
|
|
||||||
double dy = 0, dx = 0;
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
if (dx >= 750.0) {
|
|
||||||
dx = 0.0;
|
|
||||||
dy += 100.0;
|
|
||||||
}
|
|
||||||
expect(tester.getTopLeft(find.text('Item $i')), Offset(dx, dy));
|
|
||||||
dx += 150;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Test wrap to see nothing affected if elements are deleted.',
|
|
||||||
(WidgetTester tester) async {
|
|
||||||
late StateSetter stateSetter;
|
|
||||||
final List<Widget> children = List<Widget>.generate(
|
|
||||||
10,
|
|
||||||
(int index) => SizedBox(
|
|
||||||
height: index.isEven ? 100 : 50,
|
|
||||||
width: index.isEven ? 100 : 180,
|
|
||||||
child: Text('Item $index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: StatefulBuilder(
|
|
||||||
builder: (BuildContext context, StateSetter setState) {
|
|
||||||
stateSetter = setState;
|
|
||||||
return DynamicGridView.builder(
|
|
||||||
gridDelegate: const SliverGridDelegateWithWrapping(),
|
|
||||||
itemCount: children.length,
|
|
||||||
itemBuilder: (BuildContext context, int index) => children[index],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
// See if the children are in the tree.
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
expect(find.text('Item $i'), findsOneWidget);
|
|
||||||
}
|
|
||||||
// See if they are layed properly.
|
|
||||||
double dx = 0.0, dy = 0.0;
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
if (dx >= 600) {
|
|
||||||
dx = 0.0;
|
|
||||||
dy += 100;
|
|
||||||
}
|
|
||||||
expect(tester.getTopLeft(find.text('Item $i')), Offset(dx, dy));
|
|
||||||
dx += i.isEven ? 100 : 180;
|
|
||||||
}
|
|
||||||
stateSetter(() {
|
|
||||||
// Remove children
|
|
||||||
children.removeAt(0);
|
|
||||||
children.removeAt(8);
|
|
||||||
children.removeAt(5);
|
|
||||||
});
|
|
||||||
|
|
||||||
await tester.pump();
|
|
||||||
|
|
||||||
// See if the proper widgets are in the tree.
|
|
||||||
expect(find.text('Item 0'), findsNothing);
|
|
||||||
expect(find.text('Item 6'), findsNothing);
|
|
||||||
expect(find.text('Item 9'), findsNothing);
|
|
||||||
expect(find.text('Item 1'), findsOneWidget);
|
|
||||||
expect(find.text('Item 2'), findsOneWidget);
|
|
||||||
expect(find.text('Item 3'), findsOneWidget);
|
|
||||||
expect(find.text('Item 4'), findsOneWidget);
|
|
||||||
expect(find.text('Item 5'), findsOneWidget);
|
|
||||||
expect(find.text('Item 7'), findsOneWidget);
|
|
||||||
expect(find.text('Item 8'), findsOneWidget);
|
|
||||||
|
|
||||||
// See if the proper widgets are in the tree.
|
|
||||||
expect(tester.getTopLeft(find.text('Item 1')), const Offset(0.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 2')), const Offset(180.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 3')), const Offset(280.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 4')), const Offset(460.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 5')), const Offset(560.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 7')), const Offset(0.0, 100.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 8')), const Offset(180.0, 100.0));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Test wrap in Axis.vertical direction',
|
|
||||||
(WidgetTester tester) async {
|
|
||||||
final List<Widget> children = List<Widget>.generate(
|
|
||||||
5,
|
|
||||||
(int index) => SizedBox(
|
|
||||||
height: index.isEven ? 100 : 50,
|
|
||||||
width: index.isEven ? 100 : 180,
|
|
||||||
child: Text('Item $index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: DynamicGridView.builder(
|
|
||||||
itemCount: children.length,
|
|
||||||
gridDelegate: const SliverGridDelegateWithWrapping(),
|
|
||||||
itemBuilder: (BuildContext context, int index) => children[index],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Change the size of the screen
|
|
||||||
await tester.binding.setSurfaceSize(const Size(500, 100));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
expect(find.text('Item 0'), findsOneWidget);
|
|
||||||
expect(find.text('Item 1'), findsOneWidget);
|
|
||||||
expect(find.text('Item 2'), findsOneWidget);
|
|
||||||
expect(tester.getTopLeft(find.text('Item 0')), const Offset(0.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 1')), const Offset(100.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 2')), const Offset(280.0, 0.0));
|
|
||||||
expect(find.text('Item 3'), findsNothing);
|
|
||||||
expect(find.text('Item 4'), findsNothing);
|
|
||||||
await tester.binding.setSurfaceSize(const Size(560, 100));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
expect(find.text('Item 3'), findsOneWidget);
|
|
||||||
expect(tester.getTopLeft(find.text('Item 3')), const Offset(380.0, 0.0));
|
|
||||||
expect(find.text('Item 4'), findsNothing);
|
|
||||||
await tester.binding.setSurfaceSize(const Size(280, 100));
|
|
||||||
// resets the screen to its original size after the test end
|
|
||||||
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
expect(find.text('Item 0'), findsOneWidget);
|
|
||||||
expect(find.text('Item 1'), findsOneWidget);
|
|
||||||
expect(tester.getTopLeft(find.text('Item 0')), const Offset(0.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 1')), const Offset(100.0, 0.0));
|
|
||||||
expect(find.text('Item 2'), findsNothing);
|
|
||||||
expect(find.text('Item 3'), findsNothing);
|
|
||||||
expect(find.text('Item 4'), findsNothing);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Test wrap in Axis.horizontal direction',
|
|
||||||
(WidgetTester tester) async {
|
|
||||||
final List<Widget> children = List<Widget>.generate(
|
|
||||||
5,
|
|
||||||
(int index) => SizedBox(
|
|
||||||
height: index.isEven ? 100 : 50,
|
|
||||||
width: index.isEven ? 100 : 180,
|
|
||||||
child: Text('Item $index'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: DynamicGridView.wrap(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
children: children,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Change the size of the screen
|
|
||||||
await tester.binding.setSurfaceSize(const Size(180, 150));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(find.text('Item 0'), findsOneWidget);
|
|
||||||
expect(find.text('Item 1'), findsOneWidget);
|
|
||||||
expect(tester.getTopLeft(find.text('Item 0')), const Offset(0.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 1')), const Offset(0.0, 100.0));
|
|
||||||
|
|
||||||
expect(find.text('Item 2'), findsNothing);
|
|
||||||
expect(find.text('Item 3'), findsNothing);
|
|
||||||
|
|
||||||
await tester.binding.setSurfaceSize(const Size(180, 400));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(find.text('Item 0'), findsOneWidget);
|
|
||||||
expect(find.text('Item 1'), findsOneWidget);
|
|
||||||
expect(find.text('Item 2'), findsOneWidget);
|
|
||||||
expect(find.text('Item 3'), findsOneWidget);
|
|
||||||
expect(find.text('Item 4'), findsOneWidget);
|
|
||||||
|
|
||||||
expect(tester.getTopLeft(find.text('Item 0')), const Offset(0.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 1')), const Offset(0.0, 100.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 2')), const Offset(0.0, 150.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 3')), const Offset(0.0, 250.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 4')), const Offset(0.0, 300.0));
|
|
||||||
|
|
||||||
await tester.binding.setSurfaceSize(const Size(560, 100));
|
|
||||||
// resets the screen to its original size after the test end
|
|
||||||
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(find.text('Item 0'), findsOneWidget);
|
|
||||||
expect(find.text('Item 1'), findsOneWidget);
|
|
||||||
expect(find.text('Item 2'), findsOneWidget);
|
|
||||||
expect(find.text('Item 3'), findsOneWidget);
|
|
||||||
expect(find.text('Item 4'), findsNothing);
|
|
||||||
|
|
||||||
expect(tester.getTopLeft(find.text('Item 0')), const Offset(0.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 1')), const Offset(100.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 2')), const Offset(280.0, 0.0));
|
|
||||||
expect(tester.getTopLeft(find.text('Item 3')), const Offset(380.0, 0.0));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget textFieldBoilerplate({required Widget child}) {
|
|
||||||
return MaterialApp(
|
|
||||||
home: Localizations(
|
|
||||||
locale: const Locale('en', 'US'),
|
|
||||||
delegates: <LocalizationsDelegate<dynamic>>[
|
|
||||||
WidgetsLocalizationsDelegate(),
|
|
||||||
MaterialLocalizationsDelegate(),
|
|
||||||
],
|
|
||||||
child: Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: MediaQuery(
|
|
||||||
data: const MediaQueryData(size: Size(800.0, 600.0)),
|
|
||||||
child: Center(
|
|
||||||
child: Material(
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class MaterialLocalizationsDelegate
|
|
||||||
extends LocalizationsDelegate<MaterialLocalizations> {
|
|
||||||
@override
|
|
||||||
bool isSupported(Locale locale) => true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<MaterialLocalizations> load(Locale locale) =>
|
|
||||||
DefaultMaterialLocalizations.load(locale);
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldReload(MaterialLocalizationsDelegate old) => false;
|
|
||||||
}
|
|
||||||
|
|
||||||
class WidgetsLocalizationsDelegate
|
|
||||||
extends LocalizationsDelegate<WidgetsLocalizations> {
|
|
||||||
@override
|
|
||||||
bool isSupported(Locale locale) => true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<WidgetsLocalizations> load(Locale locale) =>
|
|
||||||
DefaultWidgetsLocalizations.load(locale);
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldReload(WidgetsLocalizationsDelegate old) => false;
|
|
||||||
}
|
|
Reference in New Issue
Block a user