| // 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; |
| } |
| } |