95 lines
2.7 KiB
Dart
95 lines
2.7 KiB
Dart
|
|
import 'package:flutter/material.dart';
|
||
|
|
|
||
|
|
typedef AnimatedGridBuilder<T> = Widget Function(BuildContext, T item);
|
||
|
|
|
||
|
|
class AnimateGrid<T> extends StatelessWidget {
|
||
|
|
final int columns;
|
||
|
|
final double itemHeight;
|
||
|
|
final double gap;
|
||
|
|
final List<T> items;
|
||
|
|
final Key Function(T item) keyBuilder;
|
||
|
|
final AnimatedGridBuilder<T> builder;
|
||
|
|
final Duration duration;
|
||
|
|
final Curve curve;
|
||
|
|
|
||
|
|
const AnimateGrid({
|
||
|
|
super.key,
|
||
|
|
required this.items,
|
||
|
|
required this.itemHeight,
|
||
|
|
required this.keyBuilder,
|
||
|
|
required this.builder,
|
||
|
|
this.gap = 8,
|
||
|
|
this.duration = const Duration(milliseconds: 300),
|
||
|
|
this.curve = Curves.easeOut,
|
||
|
|
this.columns = 2,
|
||
|
|
});
|
||
|
|
|
||
|
|
int _rows(int columns, int count) => (count / columns).ceil();
|
||
|
|
|
||
|
|
Offset _getOffset(
|
||
|
|
int index,
|
||
|
|
int count,
|
||
|
|
double itemWidth,
|
||
|
|
double itemHeight,
|
||
|
|
) {
|
||
|
|
final xIndex = index % columns;
|
||
|
|
final yIndex = (index / columns).floor();
|
||
|
|
return Offset(
|
||
|
|
xIndex * itemWidth + xIndex * gap, yIndex * itemHeight + yIndex * gap);
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
return LayoutBuilder(builder: (_, constraints) {
|
||
|
|
assert(constraints.hasBoundedHeight == false);
|
||
|
|
final gapWidth = (columns - 1) * gap;
|
||
|
|
final width = constraints.maxWidth;
|
||
|
|
final itemWidth = (width - gapWidth) / columns;
|
||
|
|
final count = items.length;
|
||
|
|
final rows = _rows(columns, count);
|
||
|
|
final gapHeight = (rows - 1) * gap;
|
||
|
|
final height = rows * itemHeight + gapHeight;
|
||
|
|
return SizedBox(
|
||
|
|
width: width,
|
||
|
|
height: height,
|
||
|
|
child: Stack(
|
||
|
|
children: [
|
||
|
|
for (var i = 0; i <= count - 1; i++)
|
||
|
|
Builder(
|
||
|
|
key: keyBuilder(items[i]),
|
||
|
|
builder: (context) {
|
||
|
|
final item = items[i];
|
||
|
|
final offset = _getOffset(
|
||
|
|
i,
|
||
|
|
count,
|
||
|
|
itemWidth,
|
||
|
|
itemHeight,
|
||
|
|
);
|
||
|
|
return TweenAnimationBuilder(
|
||
|
|
tween: Tween<Offset>(end: offset),
|
||
|
|
duration: duration,
|
||
|
|
curve: curve,
|
||
|
|
builder: (_, offset, child) {
|
||
|
|
return Transform.translate(
|
||
|
|
offset: offset,
|
||
|
|
child: child,
|
||
|
|
);
|
||
|
|
},
|
||
|
|
child: SizedBox(
|
||
|
|
height: itemHeight,
|
||
|
|
width: itemWidth,
|
||
|
|
child: builder(
|
||
|
|
context,
|
||
|
|
item,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
},
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|