Last active
July 12, 2021 12:28
-
-
Save iamSahdeep/9272d79feea33d29be64eb45d48b4ae1 to your computer and use it in GitHub Desktop.
AnimatedGrid to convert Grid into List with Animation. Somewhat like this : https://i.stack.imgur.com/HtR9K.gif
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// AnimatedGrid : Transforming Grid to List | |
// Somewhat like this : https://i.stack.imgur.com/HtR9K.gif | |
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Demo', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: MyHomePage(title: 'Flutter Demo Home Page'), | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
MyHomePage({Key? key, required this.title}) : super(key: key); | |
final String title; | |
@override | |
_MyHomePageState createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
int _column = 1; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text(widget.title), | |
), | |
body: SingleChildScrollView( | |
child: AnimatedGrid<String>( | |
itemHeight: 100, | |
columns: _column, | |
items: [ | |
'1', | |
'2', | |
'3', | |
'4', | |
'5', | |
'6', | |
'7', | |
'8', | |
'9', | |
'10', | |
'11', | |
'12', | |
'13', | |
'14', | |
'15', | |
'16', | |
'17', | |
'18', | |
'19', | |
'20', | |
'21', | |
'22', | |
'23', | |
'24' | |
], | |
keyBuilder: (_) => ValueKey(_), | |
builder: (ctx, item, details) { | |
return Container( | |
color: Colors.red.withOpacity(int.parse(item) / 30), | |
child: Center( | |
child: Text(item), | |
), | |
); | |
}, | |
), | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () { | |
setState(() { | |
_column = _column == 1 ? 2 : 1; | |
}); | |
}, | |
tooltip: 'lol', | |
child: Icon(Icons.check), | |
), // This trailing comma makes auto-formatting nicer for build methods. | |
); | |
} | |
} | |
typedef AnimatedGridBuilder<T> = Widget Function( | |
BuildContext, T item, AnimatedGridDetails details); | |
class AnimatedGrid<T> extends StatelessWidget { | |
const AnimatedGrid({ | |
Key? key, | |
required this.itemHeight, | |
required this.items, | |
required this.keyBuilder, | |
required this.builder, | |
this.columns = 2, | |
this.duration = const Duration(milliseconds: 750), | |
this.curve = Curves.easeInOut, | |
}) : super(key: key); | |
/// The grid items. Should all be the same height. | |
final List<T> items; | |
/// Construct keys given the item provided. Each key must be unique. | |
final Key Function(T item) keyBuilder; | |
/// Build a widget given a context, the current item, and the column and row index. | |
final AnimatedGridBuilder<T> builder; | |
/// The number of columns wide to display. | |
final int columns; | |
/// The height of each child. | |
final double itemHeight; | |
/// The duration of the sort animation. | |
final Duration duration; | |
/// The curve of the sort animation. | |
final Curve curve; | |
static int _rows(int columns, int count) => (count / columns).ceil(); | |
static List<int> gridIndicies(int index, int columns, int count) { | |
final rows = _rows(columns, count); | |
final maxItemsForGridSize = columns * rows; | |
final yIndex = (index / maxItemsForGridSize * rows).floor(); | |
final xIndex = index % columns; | |
return [xIndex, yIndex]; | |
} | |
@override | |
Widget build(BuildContext context) { | |
return LayoutBuilder( | |
builder: (context, constraints) { | |
assert(constraints.hasBoundedWidth); | |
assert(constraints.hasBoundedHeight == false); | |
final width = constraints.maxWidth; | |
final count = items.length; | |
final itemWidth = width / columns; | |
final rows = _rows(columns, count); | |
final gridHeight = rows * itemHeight; | |
return AnimatedContainer( | |
duration: duration, | |
height: gridHeight, | |
width: itemWidth * columns, | |
child: Stack( | |
alignment: Alignment.topLeft, | |
children: [ | |
for (var i = 0; i <= items.length - 1; i++) | |
Builder( | |
key: keyBuilder(items[i]), | |
builder: (context) { | |
final item = items[i]; | |
final indicies = gridIndicies(i, columns, count); | |
assert(indicies.length == 2); | |
final xIndex = indicies.first; | |
final yIndex = indicies.last; | |
final offset = | |
Offset(xIndex * itemWidth, yIndex * itemHeight); | |
return TweenAnimationBuilder( | |
tween: Tween<Offset>(end: offset), | |
duration: duration, | |
curve: curve, | |
builder: (context, Offset offset, child) { | |
print(offset); | |
return Transform.translate( | |
offset: offset, | |
child: child, | |
); | |
}, | |
child: AnimatedContainer( | |
duration: duration, | |
height: itemHeight, | |
width: itemWidth, | |
child: builder( | |
context, | |
item, | |
AnimatedGridDetails( | |
index: i, | |
columnIndex: xIndex, | |
rowIndex: yIndex, | |
columns: columns, | |
rows: rows, | |
), | |
), | |
), | |
); | |
}, | |
), | |
], | |
), | |
); | |
}, | |
); | |
} | |
} | |
class AnimatedGridDetails { | |
/// A collection of details currently being used by [AnimatedGrid] | |
AnimatedGridDetails({ | |
required this.index, | |
required this.columnIndex, | |
required this.rowIndex, | |
required this.columns, | |
required this.rows, | |
}); | |
/// The current index | |
final int index; | |
/// The current column index | |
final int columnIndex; | |
/// The current row index | |
final int rowIndex; | |
/// The number of columns | |
final int columns; | |
/// The number of rows | |
final int rows; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment