animations based on the number of items and not the number of clicks
Closed this issue · 3 comments
andre-ab commented
andre-ab commented
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());
}
andre-ab commented
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;
}
}
omerbyrk commented
It seems you already solved the issue. I am closing the issue. Thank you.