refactor!: initial attachments refactor.
Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/src/utils/utils.dart';
|
||||
|
||||
/// {@template matrix}
|
||||
/// A matrix represents a 2D array of integers.
|
||||
/// {@endtemplate}
|
||||
typedef Matrix = List<List<int>>;
|
||||
|
||||
extension on Matrix {
|
||||
/// Returns the total number of items in the matrix.
|
||||
int get count => fold(0, (sum, row) => sum + row.length);
|
||||
|
||||
/// Creates a lazy iterable of the [count] first elements of this iterable.
|
||||
///
|
||||
/// The returned `Iterable` may contain fewer than `count` elements, if `this`
|
||||
/// contains fewer than `count` elements.
|
||||
Matrix takeItems(int count) {
|
||||
final matrix = [...this];
|
||||
|
||||
// Remove items from the end of the matrix until the count is equal to n.
|
||||
while (matrix.count > count) {
|
||||
matrix.last.removeLast();
|
||||
if (matrix.last.isEmpty) {
|
||||
matrix.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
}
|
||||
|
||||
/// Signature for a function that builds a widget for the overlay of the last
|
||||
/// child in the grid in case the number of children exceeds the maximum.
|
||||
///
|
||||
/// The [remaining] parameter represents the number of children that are not
|
||||
/// displayed in the grid.
|
||||
typedef OverlayBuilder = Widget Function(BuildContext context, int remaining);
|
||||
|
||||
/// {@template flex_grid}
|
||||
/// A flexible grid widget that arranges its children based on a provided
|
||||
/// [pattern].
|
||||
///
|
||||
/// The [FlexGrid] widget displays a grid of [children] widgets based on a
|
||||
/// provided [pattern]. Each numeric value in the matrix represents the
|
||||
/// flex value of the corresponding widget in the grid. The number of widgets
|
||||
/// must match the number of cells in the matrix.
|
||||
///
|
||||
/// The grid can be configured to have a maximum number of children to display.
|
||||
/// If the number of children exceeds the maximum, the last child will show the
|
||||
/// remaining number of children as a count in an overlay. An overlay builder
|
||||
/// can be provided to customize the overlay for the last child.
|
||||
///
|
||||
/// The direction of the grid can be reversed, with either the column or row as
|
||||
/// the primary direction. Spacing can be applied between children in the main
|
||||
/// axis and between the runs (rows or columns) themselves in the cross axis.
|
||||
///
|
||||
/// Example usage:
|
||||
/// ```dart
|
||||
/// FlexGrid(
|
||||
/// pattern: const [
|
||||
/// [1, 1],
|
||||
/// [1, 1],
|
||||
/// ],
|
||||
/// children: [
|
||||
/// Container(color: Colors.red),
|
||||
/// Container(color: Colors.blue),
|
||||
/// Container(color: Colors.green),
|
||||
/// Container(color: Colors.yellow),
|
||||
/// ],
|
||||
/// )
|
||||
/// ```
|
||||
/// {@endtemplate}
|
||||
class FlexGrid extends StatelessWidget {
|
||||
/// {@macro flex_grid}
|
||||
FlexGrid({
|
||||
super.key,
|
||||
required this.pattern,
|
||||
required this.children,
|
||||
this.maxChildren,
|
||||
this.overlayBuilder,
|
||||
this.reverse = false,
|
||||
this.spacing = 2.0,
|
||||
this.runSpacing = 2.0,
|
||||
}) : assert(
|
||||
pattern.count == children.length,
|
||||
'The number of children must match the number of cells in the matrix',
|
||||
),
|
||||
assert(
|
||||
maxChildren == null || maxChildren <= pattern.count,
|
||||
'The number of maxChildren must be less than or equal to the number '
|
||||
'of cells in the matrix',
|
||||
),
|
||||
assert(
|
||||
maxChildren == null || overlayBuilder != null,
|
||||
'overlayBuilder must be provided when maxChildren is not null',
|
||||
);
|
||||
|
||||
/// The pattern of the grid.
|
||||
///
|
||||
/// Each numeric value in the array represents the flex value of
|
||||
/// corresponding widget in grid.
|
||||
///
|
||||
/// For example, a grid with 2 rows and 2 columns can be represented as:
|
||||
///
|
||||
/// ```dart
|
||||
/// [
|
||||
/// [1, 1],
|
||||
/// [1, 1],
|
||||
/// ]
|
||||
/// ```
|
||||
///
|
||||
/// This will create a grid with 4 cells with each cell having a flex value
|
||||
/// of 1.
|
||||
final Matrix pattern;
|
||||
|
||||
/// The widgets to display in the grid.
|
||||
///
|
||||
/// The number of widgets must match the number of cells in the matrix.
|
||||
final List<Widget> children;
|
||||
|
||||
/// Whether to reverse the direction of the grid.
|
||||
///
|
||||
/// By default, the grid is rendered with column as primary direction and row
|
||||
/// as secondary direction.
|
||||
///
|
||||
/// If this is set to `true`, the grid will be rendered with row as primary
|
||||
/// direction and column as secondary direction.
|
||||
final bool reverse;
|
||||
|
||||
/// The maximum number of children to display in the grid.
|
||||
///
|
||||
/// If this is set, the grid will be rendered with a maximum of [maxChildren]
|
||||
/// children. If the number of children is greater than [maxChildren], The
|
||||
/// last child will show the remaining number of children as a count in a
|
||||
/// overlay.
|
||||
final int? maxChildren;
|
||||
|
||||
/// The builder to use to build the overlay for the last child in case the
|
||||
/// number of children is greater than [maxChildren].
|
||||
///
|
||||
/// The builder will be passed the number of remaining children to display.
|
||||
final OverlayBuilder? overlayBuilder;
|
||||
|
||||
/// How much space to place between children in a run in the main axis.
|
||||
///
|
||||
/// For example, if [spacing] is 10.0, the children will be spaced at least
|
||||
/// 10.0 logical pixels apart in the main axis.
|
||||
///
|
||||
/// Defaults to 2.0.
|
||||
final double spacing;
|
||||
|
||||
/// How much space to place between the runs themselves in the cross axis.
|
||||
///
|
||||
/// For example, if [runSpacing] is 10.0, the runs will be spaced at least
|
||||
/// 10.0 logical pixels apart in the cross axis.
|
||||
///
|
||||
/// Defaults to 2.0.
|
||||
final double runSpacing;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Determine the primary and secondary directions.
|
||||
final primaryDirection = reverse ? Axis.horizontal : Axis.vertical;
|
||||
final secondaryDirection = reverse ? Axis.vertical : Axis.horizontal;
|
||||
|
||||
var pattern = [...this.pattern];
|
||||
var children = [...this.children];
|
||||
|
||||
// If the number of children is greater than the maximum number of children,
|
||||
// remove the extra children.
|
||||
final maxChildren = this.maxChildren;
|
||||
if (maxChildren != null && maxChildren < pattern.count) {
|
||||
children = [...children.take(maxChildren)];
|
||||
pattern = [...pattern.takeItems(maxChildren)];
|
||||
}
|
||||
|
||||
// Track the current child index.
|
||||
//
|
||||
// This is used to determine which child to render next.
|
||||
var childIndex = 0;
|
||||
|
||||
return Flex(
|
||||
direction: primaryDirection,
|
||||
children: <Widget>[
|
||||
for (final row in pattern)
|
||||
Expanded(
|
||||
child: Flex(
|
||||
direction: secondaryDirection,
|
||||
children: <Widget>[
|
||||
...row.map((flex) {
|
||||
final isLastChild = childIndex == children.length - 1;
|
||||
|
||||
// Determine the number of remaining children.
|
||||
final remaining = (this.children.length - 1) - childIndex;
|
||||
|
||||
// If we are at the last child and there are remaining
|
||||
// children, show the remaining number of children as a
|
||||
// count in a overlay.
|
||||
if (isLastChild && remaining > 0) {
|
||||
return Expanded(
|
||||
flex: flex,
|
||||
child: Stack(
|
||||
fit: StackFit.passthrough,
|
||||
children: [
|
||||
children[childIndex++],
|
||||
overlayBuilder!.call(context, remaining),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Otherwise, return the child.
|
||||
return Expanded(
|
||||
flex: flex,
|
||||
child: children[childIndex++],
|
||||
);
|
||||
}),
|
||||
].insertBetween(
|
||||
Gap(direction: secondaryDirection, spacing: runSpacing),
|
||||
),
|
||||
),
|
||||
),
|
||||
].insertBetween(
|
||||
Gap(direction: primaryDirection, spacing: spacing),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template gap}
|
||||
/// A gap widget used to add spacing between children in either the horizontal
|
||||
/// or vertical direction.
|
||||
/// {@endtemplate}
|
||||
class Gap extends StatelessWidget {
|
||||
/// {@macro gap}
|
||||
const Gap({
|
||||
super.key,
|
||||
required this.direction,
|
||||
this.spacing = 0.0,
|
||||
});
|
||||
|
||||
/// The direction of the gap.
|
||||
final Axis direction;
|
||||
|
||||
/// The spacing between children in the gap.
|
||||
///
|
||||
/// Defaults to 0.0.
|
||||
final double spacing;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
switch (direction) {
|
||||
case Axis.horizontal:
|
||||
return SizedBox(width: spacing);
|
||||
case Axis.vertical:
|
||||
return SizedBox(height: spacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user