Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make switch animation of two GridView elements in Flutter

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).

like image 963
GLodi Avatar asked Feb 18 '19 10:02

GLodi


1 Answers

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).

like image 131
GLodi Avatar answered Sep 22 '22 13:09

GLodi