blob: 8eedfe40c310214c74f85f0abaabe101002169da [file] [log] [blame]
// 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/widgets.dart';
import 'render_dynamic_grid.dart';
import 'staggered_layout.dart';
import 'wrap_layout.dart';
/// A scrollable grid of widgets, capable of dynamically positioning tiles based
/// on different aspects of the children's size when laid out with loose
/// constraints.
///
/// Three [SliverGridDelegate]s support wrapping or staggering the dynamically
/// sized children of the grid. Staggering children can use
/// [SliverGridDelegateWithFixedCrossAxisCount] or
/// [SliverGridDelegateWithMaxCrossAxisExtent], while wrapping uses its own
/// [SliverGridDelegateWithWrapping].
///
/// {@macro dynamicLayouts.garbageCollection}
///
/// 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'),
/// ),
/// ),
/// ],
/// ),
/// ```
///
/// This sample code shows how to use the [DynamicGridView.staggered]
/// constructor with a [maxCrossAxisExtent]:
///
/// ```dart
/// DynamicGridView.staggered(
/// maxCrossAxisExtent: 100,
/// crossAxisSpacing: 2,
/// mainAxisSpacing: 2,
/// children: List.generate(
/// 50,
/// (int index) => Container(
/// height: index % 3 * 50 + 20,
/// color: Colors.amber[index % 9 * 100],
/// child: Center(child: Text("Index $index")),
/// ),
/// ),
/// );
/// ```
class DynamicGridView extends GridView {
/// Creates a scrollable, 2D array of widgets with a custom
/// [SliverGridDelegate].
DynamicGridView({
super.key,
super.scrollDirection,
super.reverse,
required super.gridDelegate,
// This creates a SliverChildListDelegate in the super class.
super.children = const <Widget>[],
});
/// Creates a scrollable, 2D array of widgets that are created on demand.
DynamicGridView.builder({
super.key,
super.scrollDirection,
super.reverse,
required super.gridDelegate,
// This creates a SliverChildBuilderDelegate in the super class.
required super.itemBuilder,
super.itemCount,
}) : super.builder();
/// Creates a scrollable, 2D array of widgets with tiles where each tile can
/// 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,
),
);
/// Creates a scrollable, 2D array of widgets where each tile's main axis
/// extent will be determined by the child's corresponding finite size and
/// the cross axis extent will be fixed, generating a staggered layout.
///
/// Either a [crossAxisCount] or a [maxCrossAxisExtent] must be provided.
/// The constructor will then use a
/// [DynamicSliverGridDelegateWithFixedCrossAxisCount] or a
/// [DynamicSliverGridDelegateWithMaxCrossAxisExtent] as its [gridDelegate],
/// respectively.
///
/// This sample code shows how to use the constructor with a
/// [maxCrossAxisExtent] and a simple layout:
///
/// ```dart
/// DynamicGridView.staggered(
/// maxCrossAxisExtent: 100,
/// crossAxisSpacing: 2,
/// mainAxisSpacing: 2,
/// children: List.generate(
/// 50,
/// (int index) => Container(
/// height: index % 3 * 50 + 20,
/// color: Colors.amber[index % 9 * 100],
/// child: Center(child: Text("Index $index")),
/// ),
/// ),
/// );
/// ```
DynamicGridView.staggered({
super.key,
super.scrollDirection,
super.reverse,
int? crossAxisCount,
double? maxCrossAxisExtent,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
super.children = const <Widget>[],
}) : assert(crossAxisCount != null || maxCrossAxisExtent != null),
assert(crossAxisCount == null || maxCrossAxisExtent == null),
super(
gridDelegate: crossAxisCount != null
? DynamicSliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
)
: DynamicSliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: maxCrossAxisExtent!,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
),
);
@override
Widget buildChildLayout(BuildContext context) {
return DynamicSliverGrid(
delegate: childrenDelegate,
gridDelegate: gridDelegate,
);
}
}
/// A sliver that places multiple box children in a two dimensional arrangement.
class DynamicSliverGrid extends SliverMultiBoxAdaptorWidget {
/// Creates a sliver that places multiple box children in a two dimensional
/// arrangement.
const DynamicSliverGrid({
super.key,
required super.delegate,
required this.gridDelegate,
});
/// The delegate that manages the size and position of the children.
final SliverGridDelegate gridDelegate;
@override
RenderDynamicSliverGrid createRenderObject(BuildContext context) {
final SliverMultiBoxAdaptorElement element =
context as SliverMultiBoxAdaptorElement;
return RenderDynamicSliverGrid(
childManager: element, gridDelegate: gridDelegate);
}
@override
void updateRenderObject(
BuildContext context,
RenderDynamicSliverGrid renderObject,
) {
renderObject.gridDelegate = gridDelegate;
}
}