Animation - "Toss" Add to Cart Animation is available in flutter?

Viewed 2352

Is anyone developed animation like the following in flutter? When we click on that add to cart that transition kind animation I need. Let me know if any plugin is available for this. Appreciate for your help. https://codepen.io/designcouch/pen/OJPdZxg

DEMO:

$(document).ready(function(){
  $('#addtocart').on('click',function(){
    
    var button = $(this);
    var cart = $('#cart');
    var cartTotal = cart.attr('data-totalitems');
    var newCartTotal = parseInt(cartTotal) + 1;
    
    button.addClass('sendtocart');
    setTimeout(function(){
      button.removeClass('sendtocart');
      cart.addClass('shake').attr('data-totalitems', newCartTotal);
      setTimeout(function(){
        cart.removeClass('shake');
      },500)
    },1000)
  })
})
html, body {
  height: 100%;
  min-height: 100%;
  font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif;
}
*, *:before, *:after {
  box-sizing: border-box;
}
.page-wrapper {
  min-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.page-wrapper button {
  padding: 20px;
  border: none;
  background: #d5d8e7;
  position: relative;
  outline: none;
  border-radius: 5px;
  color: #292d48;
  font-size: 18px;
}
.page-wrapper button .cart-item {
  position: absolute;
  height: 24px;
  width: 24px;
  top: -10px;
  right: -10px;
}
.page-wrapper button .cart-item:before {
  content: '1';
  display: block;
  line-height: 24px;
  height: 24px;
  width: 24px;
  font-size: 12px;
  font-weight: 600;
  background: #2bd156;
  color: white;
  border-radius: 20px;
  text-align: center;
}
.page-wrapper button.sendtocart .cart-item {
  display: block;
  animation: xAxis 1s forwards cubic-bezier(1, 0.44, 0.84, 0.165);
}
.page-wrapper button.sendtocart .cart-item:before {
  animation: yAxis 1s alternate forwards cubic-bezier(0.165, 0.84, 0.44, 1);
}
.cart {
  position: fixed;
  top: 20px;
  right: 20px;
  width: 50px;
  height: 50px;
  background: #292d48;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 5px;
}
.cart i {
  font-size: 25px;
  color: white;
}
.cart:before {
  content: attr(data-totalitems);
  font-size: 12px;
  font-weight: 600;
  position: absolute;
  top: -12px;
  right: -12px;
  background: #2bd156;
  line-height: 24px;
  padding: 0 5px;
  height: 24px;
  min-width: 24px;
  color: white;
  text-align: center;
  border-radius: 24px;
}
.cart.shake {
  animation: shakeCart 0.4s ease-in-out forwards;
}
@keyframes xAxis {
  100% {
    transform: translateX(calc(50vw - 105px));
  }
}
@keyframes yAxis {
  100% {
    transform: translateY(calc(-50vh + 75px));
  }
}
@keyframes shakeCart {
  25% {
    transform: translateX(6px);
  }
  50% {
    transform: translateX(-4px);
  }
  75% {
    transform: translateX(2px);
  }
  100% {
    transform: translateX(0);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="cart" class="cart" data-totalitems="0">
  <i class="fas fa-shopping-cart"></i>
</div>

<div class="page-wrapper">
  <button id="addtocart">
    Add to Cart
    <span class="cart-item"></span>
  </button>
</div>

3 Answers
class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  AnimationController _animationController;
double x,y;
  @override
  void initState() {
    x =0;
    y =0;
    _animationController = AnimationController(
      duration: const Duration(seconds: 5),
      vsync: this,
    );

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
 
    return Scaffold(
      body: Center(
      
          child: Container(
        child: ButtonTheme(
            buttonColor: Colors.blue,
            child: Stack(children: [
              AnimatedBuilder(
                animation: _animationController,
                child: Container(
                  child: Padding(
                    padding: const EdgeInsets.only(left: 13.0, top: 4),
                    child: RaisedButton(
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(25.0),
                        side: BorderSide(
                          color: Colors.white,
                        ),
                      ),
                      child: new Padding(
                        padding: EdgeInsets.only(bottom: 3),
                        child: new Text(
                          '1',
                          textAlign: TextAlign.center,
                        ),
                      ),
                      onPressed: () {
                        setState(() {
                          x+=100;
                          y+=100;
                          _animationController.forward();


                        });
                      },
                    ),
                  ),
                ),
                builder: (BuildContext context, Widget child) {
                  return Transform.translate(
                    offset: (Offset(x,y)),
                    child: child);
                },
              ),
            ])),
      )),
    );
  }
}

With the help of AnimationController, Stack and Transition widgets, the above animation can be done. We move the widget from one location to another by specifying the coordinates (if it possible without the coordinates that also will be fine)

Image before animation

enter image description here

solution based on video that I have found there https://morioh.com/p/ecb172b5de54

class CartPage extends StatefulWidget {
     
  @override
  _CartPageState createState() => _CartPageState();
}

class _CartPageState extends State<CartPage>
    with TickerProviderStateMixin {
  late AnimationController moveController;
  late Animation moveAnimation;

  Position? fromPosition;
  GlobalKey starKey = GlobalKey();

  @override
  void initState() {
    moveController = AnimationController(
        vsync: this, duration: Duration(milliseconds: 5000));
    moveAnimation =
        CurvedAnimation(parent: moveController, curve: Curves.easeInOut);

    moveController.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        print('end of animation');
        setState(() {
          fromPosition = null;
        });
      }
    });
    super.initState();
  }

  @override
  void dispose() {
    moveController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final fromKey = GlobalKey();
    return Scaffold(
      body: SafeArea(
        child: Stack(
          children: [
            Column(
              children: [
                Expanded(child: SizedBox.shrink()),
                Container(child: Icon(Icons.weekend, key: fromKey)),
                ElevatedButton(
                    onPressed: () {
                      setState(() {
                        fromPosition = getPositionByKey(fromKey);
                      });
                      moveController.reset();
                      moveController.forward();
                    },
                    child: Text('start animation')),
              ],
            ),
            Row(
              children: [
                Spacer(),
                SizedBox(
                  height: 40,
                  width: 40,
                  child: Stack(
                    children: [
                      buildFlyWidget(),
                      Container(
                          width: 24,
                          height: 24,
                          padding: EdgeInsets.all(5),
                          child:
                              Icon(Icons.shopping_cart, color: Colors.black)),
                    ],
                  ),
                )
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget buildFlyWidget() {
    if (fromPosition == null) {
      return SizedBox.shrink();
    }
    return Container(
      key: starKey,
      height: 24,
      width: 24,
      child: AnimatedBuilder(
        animation: moveAnimation,
        builder: (context, child) {
          Position currentPosition;
          double x = 0, y = 0;
          if (starKey.currentContext?.findRenderObject() != null) {
            currentPosition = getPositionByKey(starKey);
            x = fromPosition!.x - currentPosition.x;
            y = fromPosition!.y - currentPosition.y;
          }
          if (x == 0) return Container();
          return Transform.translate(
            offset: Offset(
                x * (1 - moveAnimation.value), y * (1 - moveAnimation.value)),
            child: Opacity(
              opacity: fromPosition == null ? 0.0 : 1.0,
              child: Icon(Icons.weekend_outlined, color: Colors.black),
            ),
          );
        },
      ),
    );
  }
}

Position getPositionByKey(GlobalKey key) {
  final renderObject = key.currentContext!.findRenderObject()!;
  var translation = renderObject.getTransformTo(null).getTranslation();
  final rect =
      renderObject.paintBounds.shift(Offset(translation.x, translation.y));
  return Position(rect.left, rect.top, rect.size);
}

class Position {
  double x;
  double y;
  Size size;

  Position(this.x, this.y, this.size);
}
Related