I'm trying to make a switch animation between two elements of a GridView in Flutter.
I've already tried PositionedTransition, as well as a regular animation with a Tween. The elements don't animate at all.
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _HomeScreenState();
}
}
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
child: GridView.count(
shrinkWrap: true,
crossAxisSpacing: 4,
mainAxisSpacing: 4,
crossAxisCount: 5,
children: List.generate(25, (index) {
return GestureDetector(
onVerticalDragUpdate: (drag) {
if (drag.delta.dy > 10) print('down');
if (drag.delta.dy < -10) print('up');
},
onHorizontalDragUpdate: (drag) {
if (drag.delta.dx > 10) print('right');
if (drag.delta.dx < -10) print('left');
},
onHorizontalDragEnd: /* ANIMATE NOW */,
onVerticalDragEnd: /* ANIMATE NOW */,
child: Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.all(Radius.circular(5.0))
),
),
);
}),
)
);
}
}
Ideally I would want to be able to swipe left/right/up/down on an element of the grid and switch that element with the corresponding element next to it (relative to the swipe direction).
I gave up on trying with a GridView. I did however found a way to get the same result through a Stack and SlideTransition:
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _HomeScreenState();
}
}
class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
String move;
List<Tween> _switchTween = List();
List<Animation> _switchAnim = List();
List<AnimationController> _switchAnimCont = List();
double width;
@override
void initState() {
// Add a tween and a controller for each element
for (int i = 0; i < 5; i++) {
_switchAnimCont.add(AnimationController(
vsync: this,
duration: const Duration(milliseconds: 100),
));
_switchTween.add(Tween<Offset>(begin: Offset.zero, end: Offset(0, 1)));
_switchAnim.add(_switchTween[i].animate(_switchAnimCont[i]));
}
super.initState();
}
@override
Widget build(BuildContext context) {
width = MediaQuery.of(context).size.width/5;
return Stack(
children: <Widget>[
square(0, 0, 0),
square(1, width, 0),
square(2, 2*width, 0),
square(3, 3*width, 0),
square(4, 4*width, 0),
],
);
}
Widget square(int index, double left, double bottom) {
return Positioned(
left: left,
bottom: bottom,
width: width,
height: width,
child: SlideTransition(
position: _switchAnim[index],
child: GestureDetector(
// Figure out swipe's direction
onHorizontalDragUpdate: (drag) {
if (drag.delta.dx > 10) move = 'right';
if (drag.delta.dx < -10) move = 'left';
},
onHorizontalDragEnd: (drag) {
switch(move) {
case 'right' : {moveRight(index); break;}
case 'left' : {moveLeft(index); break;}
}
},
child: Container(
margin: EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(5.0))
),
),
),
),
);
}
void moveRight(int index) {
// Move the grid element that you swiped on
_switchTween[index].end = Offset(1, 0);
_switchAnimCont[index].forward();
// Move the element next to it the opposite way
_switchTween[index+1].end = Offset(-1, 0);
_switchAnimCont[index+1].forward();
// You could need to refresh the corresponding controller to animate again
}
void moveLeft(int index) {
_switchTween[index].end = Offset(-1, 0);
_switchAnimCont[index].forward();
_switchTween[index-1].end = Offset(1, 0);
_switchAnimCont[index-1].forward();
}
}
This will draw 5 white squares, and by swiping left or right on one of them, the corresponding square will switch place with the one next to it. It could also be extended to use vertically (assuming a new row is added).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With