omerbyrk/add_to_cart_animation

animations based on the number of items and not the number of clicks

Closed this issue · 3 comments

An animation works with click,
5 animations works with five clicks, how can I add the animation according to the amount ?

image

wanted:
5 animations but with one click, because it is the quantity of items
image

I temporarily solved it like this

Future<void> listClick(GlobalKey widgetKey, int quant) async {
  for (var i = 0; i < quant; i++) {
    runAddToCartAnimation(widgetKey);
    await Future.delayed(const Duration(milliseconds: 200));
  }
 cartTotalItems += quant;
  await cartKey.currentState!.runCartAnimation((cartTotalItems).toString());
}

If you wanted to leave it as default, just make a few changes:

If you don't want the animation to drag the widget to the cart, null safety is in both methods
createAddToCartAnimation
If you want the animation to be according to the quantity that will be added to the cart
createAnimationByQty

class AddToCartAnimation


final Function(Future<void> Function(GlobalKey))? createAddToCartAnimation;

  /// It's the same principle as [createAddToCartAnimation] the only difference
  /// is that the animation  will be multiplied according to the amount passed.
  final Function(Future<void> Function(GlobalKey, int))? createAnimationByQty;

null safety check

  
    @override
  void initState() {
    if (this.widget.createAddToCartAnimation != null) {
      this.widget.createAddToCartAnimation!(runAddToCartAnimation);
    }

    if (this.widget.createAnimationByQty != null) {
      this.widget.createAnimationByQty!(animationByQty);
    }
    super.initState();
  }
  

// Method to make animation according to quantity

   Future<void> animationByQty(GlobalKey widgetKey, int qty) async {
    for (var i = 0; i < qty; i++) {
      runAddToCartAnimation(widgetKey);
      await Future.delayed(const Duration(milliseconds: 200));
    }
  }

end of changes in class addToCartAnimation

Call

//  Start the variable in the controller or class MyHomePageState extends State<MyHomePage>

late Function(GlobalKey, int) animationByQty;


 // @override
 // Widget build(BuildContext context)

return AddToCartAnimation(
      // To send the library the location of the Cart icon
      cartKey: controller.cartKey,
      height: 30,
      width: 30,
      opacity: 0.85,
      dragAnimation: const DragToCartAnimationOptions(rotation: true),
      jumpAnimation: const JumpAnimationOptions(),
      
      createAnimationByQty: (animationByQty) {
        controller.animationByQty = animationByQty;
      },

Method that does the magic, can be in the controller or on the same page


  Future<void> listClick(GlobalKey widgetKey, int quant) async {
    await animationByQty(widgetKey, quant);
    cartTotalItems += quant;
    await cartKey.currentState!.runCartAnimation((cartTotalItems).toString());
  }





Full code StatefulWidget

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  MyHomePageState createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  // We can detect the location of the cart by this  GlobalKey<CartIconKey>
  GlobalKey<CartIconKey> cartKey = GlobalKey<CartIconKey>();
  // late Function(GlobalKey) runAddToCartAnimation;

  late Function(GlobalKey, int) animationByQty;
  var _cartQuantityItems = 0;

  @override
  Widget build(BuildContext context) {
    return AddToCartAnimation(
      // To send the library the location of the Cart icon
      cartKey: cartKey,
      height: 30,
      width: 30,
      opacity: 0.85,
      dragAnimation: const DragToCartAnimationOptions(
        rotation: true,
      ),
      jumpAnimation: const JumpAnimationOptions(),
      createAnimationByQty: (animationByQty) {
        animationByQty = animationByQty;
      },
      /*  createAddToCartAnimation: (runAddToCartAnimation) {
        // You can run the animation by addToCartAnimationMethod, just pass trough the the global key of  the image as parameter
        this.runAddToCartAnimation = runAddToCartAnimation;
      }, */
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
          centerTitle: false,
          actions: [
            //  Adding 'clear-cart-button'
            IconButton(
              icon: const Icon(Icons.cleaning_services),
              onPressed: () {
                _cartQuantityItems = 0;
                cartKey.currentState!.runClearCartAnimation();
              },
            ),
            const SizedBox(width: 16),
            AddToCartIcon(
              key: cartKey,
              icon: const Icon(Icons.shopping_cart),
              badgeOptions: const BadgeOptions(
                active: true,
                backgroundColor: Colors.red,
              ),
            ),
            const SizedBox(
              width: 16,
            )
          ],
        ),
        body: ListView(
          children: List.generate(
            15,
            (index) => AppListItem(
              onClick: listClick,
              index: index,
            ),
          ),
        ),
      ),
    );
  }

  Future<void> listClick(GlobalKey widgetKey, int quant) async {
    await animationByQty(widgetKey, quant);
    _cartQuantityItems += quant;
    await cartKey.currentState!
        .runCartAnimation((_cartQuantityItems).toString());
  }
  /*  
   void listClick(GlobalKey widgetKey) async {
    await runAddToCartAnimation(widgetKey);
    await cartKey.currentState!
        .runCartAnimation((++_cartQuantityItems).toString());
  }
 */
}

Full code Class AddToCartAnimation


import 'dart:math';

import 'drag_to_cart_animation_options.dart';

import 'jump_animation_options.dart';
import 'add_to_cart_icon.dart';
import 'globalkeyext.dart';
import 'package:flutter/material.dart';

export 'add_to_cart_icon.dart';
export 'jump_animation_options.dart';
export 'drag_to_cart_animation_options.dart';

class _PositionedAnimationModel {
  bool showAnimation = false;
  bool animationActive = false;
  Offset imageSourcePoint = Offset.zero;
  Offset imageDestPoint = Offset.zero;
  Size imageSourceSize = Size.zero;
  Size imageDestSize = Size.zero;
  bool rotation = false;
  double opacity = 0.85;
  late Container container;
  Duration duration = Duration.zero;
  Curve curve = Curves.easeIn;
}

/// An add to cart animation which provide you an animation by sliding the product to cart in the Flutter app
class AddToCartAnimation extends StatefulWidget {
  final Widget child;

  /// The Global Key of the [AddToCartIcon] element. We need it because we need to know where is the cart icon is located in the screen. Based on the location, we are dragging given widget to the cart.
  final GlobalKey<CartIconKey> cartKey;

  /// you can receive [runAddToCartAnimation] animation method on [createAddToCartAnimation].
  /// [runAddToCartAnimation] animation method runs the add to cart animation based on the given parameters.
  /// Add to cart animation drags the given widget to the cart based on their location via global keys
  final Function(Future<void> Function(GlobalKey))? createAddToCartAnimation;

  /// It's the same principle as [createAddToCartAnimation] the only difference
  /// is that the animation  will be multiplied according to the amount passed.
  final Function(Future<void> Function(GlobalKey, int))? createAnimationByQty;

  /// What Should the given widget's height while dragging to the cart
  final double height;

  /// What Should the given widget's width while dragging to the cart
  final double width;

  /// What Should the given widget's opacity while dragging to the cart
  final double opacity;

  /// Should the given widget jump before the dragging
  final JumpAnimationOptions jumpAnimation;

  /// The animation options while given widget sliding to cart
  final DragToCartAnimationOptions dragAnimation;

  const AddToCartAnimation({
    Key? key,
    required this.child,
    required this.cartKey,
    this.createAddToCartAnimation,
    this.createAnimationByQty,
    this.height = 30,
    this.width = 30,
    this.opacity = 0.85,
    this.jumpAnimation = const JumpAnimationOptions(),
    this.dragAnimation = const DragToCartAnimationOptions(),
  }) : super(key: key);

  @override
  _AddToCartAnimationState createState() => _AddToCartAnimationState();
}

class _AddToCartAnimationState extends State<AddToCartAnimation> {
  List<_PositionedAnimationModel> animationModels = [];

  @override
  void initState() {
    if (this.widget.createAddToCartAnimation != null) {
      this.widget.createAddToCartAnimation!(runAddToCartAnimation);
    }

    if (this.widget.createAnimationByQty != null) {
      this.widget.createAnimationByQty!(animationByQty);
    }
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        widget.child,
        Positioned.fill(
          child: Stack(
            children: animationModels
                .map<Widget>((model) => model.showAnimation
                    ? AnimatedPositioned(
                        top: model.animationActive
                            ? model.imageDestPoint.dx
                            : model.imageSourcePoint.dx,
                        left: model.animationActive
                            ? model.imageDestPoint.dy
                            : model.imageSourcePoint.dy,
                        height: model.animationActive
                            ? model.imageDestSize.height
                            : model.imageSourceSize.height,
                        width: model.animationActive
                            ? model.imageDestSize.width
                            : model.imageSourceSize.width,
                        duration: model.duration,
                        curve: model.curve,
                        child: model.rotation
                            ? TweenAnimationBuilder(
                                tween: Tween<double>(begin: 0, end: pi * 2),
                                duration: model.duration,
                                child: model.container,
                                builder: (context, double value, widget) {
                                  return Transform.rotate(
                                    angle: value,
                                    child: Opacity(
                                      opacity: model.opacity,
                                      child: widget,
                                    ),
                                  );
                                },
                              )
                            : Opacity(
                                opacity: model.opacity,
                                child: model.container,
                              ),
                      )
                    : Container())
                .toList(),
          ),
        ),
      ],
    );
  }

  Future<void> animationByQty(GlobalKey widgetKey, int qty) async {
    for (var i = 0; i < qty; i++) {
      runAddToCartAnimation(widgetKey);
      await Future.delayed(const Duration(milliseconds: 200));
    }
  }

  Future<void> runAddToCartAnimation(GlobalKey widgetKey) async {
    _PositionedAnimationModel animationModel = _PositionedAnimationModel()
      ..rotation = false
      ..opacity = widget.opacity;

    animationModel.imageSourcePoint = Offset(
        widgetKey.globalPaintBounds!.top, widgetKey.globalPaintBounds!.left);

    // Improvement/Suggestion 1: Provinding option, in order to, use/or not initial "jumping" on image
    var startingHeight = widget.jumpAnimation.active
        ? widgetKey.currentContext!.size!.height
        : 0;
    animationModel.imageDestPoint = Offset(
        widgetKey.globalPaintBounds!.top - (startingHeight + widget.height),
        widgetKey.globalPaintBounds!.left);

    animationModel.imageSourceSize = Size(widgetKey.currentContext!.size!.width,
        widgetKey.currentContext!.size!.height);

    animationModel.imageDestSize = Size(
        widgetKey.currentContext!.size!.width + widget.width,
        widgetKey.currentContext!.size!.height + widget.height);

    animationModels.add(animationModel);
    // Improvement/Suggestion 2: Changing the animationModel.child from Image to gkImageContainer
    animationModel.container = Container(
      child: (widgetKey.currentWidget! as Container).child,
    );

    animationModel.showAnimation = true;

    setState(() {});

    await Future.delayed(Duration(milliseconds: 75));

    animationModel.curve = widget.jumpAnimation.curve;
    animationModel.duration =
        widget.jumpAnimation.duration; // This is for preview mode
    animationModel.animationActive = true; // That's start the animation.
    setState(() {});

    await Future.delayed(animationModel.duration);
    // Drag to cart animation
    animationModel.curve = widget.dragAnimation.curve;
    animationModel.rotation = widget.dragAnimation.rotation;
    animationModel.duration =
        widget.dragAnimation.duration; // this is for add to button mode

    animationModel.imageDestPoint = Offset(
        this.widget.cartKey.globalPaintBounds!.top,
        this.widget.cartKey.globalPaintBounds!.left);

    animationModel.imageDestSize = Size(
        this.widget.cartKey.currentContext!.size!.width,
        this.widget.cartKey.currentContext!.size!.height);

    setState(() {});

    await Future.delayed(animationModel.duration);
    animationModel.showAnimation = false;
    animationModel.animationActive = false;

    setState(() {});

    // Improvement/Suggestion 4.3: runCartAnimation is running independently, using gkCart.currentState(main.dart)
    // await this.widget.gkCart.currentState!.runCartAnimation();

    return;
  }
}

It seems you already solved the issue. I am closing the issue. Thank you.