Files
MWClash/lib/widgets/animate_grid.dart
2024-04-30 23:38:49 +08:00

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,
),
),
);
},
),
],
),
);
});
}
}