I am trying to build a list view with cards with different animation behaviors depending on user interactions like hovering. The hovered card needs to be scaled up and the remaining cards in the list need to be scaled down and made less opaque. Like:
View code:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:simple_animations/multi_tween/multi_tween.dart';
import 'package:simple_animations/simple_animations.dart';
import 'package:simple_animations/stateless_animation/custom_animation.dart';
import '../controllers/work_controller.dart';
class WorkView extends GetView<WorkController> {
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
flex: 1,
child: Container(color: Colors.blueGrey),
),
Expanded(
flex: 2,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
10,
(index) {
return MouseRegion(
onExit: (event) => controller.hoverIndex.value = -1,
onEnter: ((event) {
print("Setting index: $index");
controller.hoverIndex.value = index;
}),
child: AnimatedWorkCard(
index: index,
));
},
),
),
))
],
);
}
}
class AnimatedWorkCard extends StatefulWidget {
final int index;
const AnimatedWorkCard({
Key? key,
required this.index,
}) : super(key: key);
@override
State<AnimatedWorkCard> createState() => _AnimatedWorkCardState();
}
enum CardAnimationProps {
isNotHoveredOpacity,
isHoveredImgScale,
isNotHoveredTranslateX,
isHoveredTextTranslateY,
isHoveredTextOpacity,
}
class _AnimatedWorkCardState extends State<AnimatedWorkCard> {
final WorkController controller = Get.find();
var animationControl = CustomAnimationControl.play;
@override
Widget build(BuildContext context) {
return Obx(() {
var isHovered = controller.hoverIndex.value == widget.index;
if (!isHovered) {
return CustomAnimation<TimelineValue<CardAnimationProps>>(
control: animationControl,
tween: createNotHoveredTween(),
builder: (context, child, value) {
return Transform.translate(
offset: Offset(
value.get(CardAnimationProps.isNotHoveredTranslateX), 0),
child: child,
);
},
child: Card(
color: Colors.amber,
child: Container(
width: Get.width * 0.2,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Work ${widget.index}',
style: TextStyle(
fontSize: Get.width * 0.03,
),
),
ElevatedButton(
child: Text('Hovering: ${controller.hoverIndex.value}'),
onPressed: () {
Get.toNamed('/work/${widget.index}');
},
),
],
),
),
),
);
} else {
return CustomAnimation<TimelineValue<CardAnimationProps>>(
control: animationControl,
tween: createTween(controller.hoverIndex.value, widget.index),
builder: (context, child, value) {
return Transform.scale(
scale: value.get(CardAnimationProps.isHoveredImgScale),
child: child ?? Container());
},
child: Card(
color: Colors.amber,
child: Container(
width: Get.width * 0.2,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Work ${widget.index}',
style: TextStyle(
fontSize: Get.width * 0.03,
),
),
ElevatedButton(
child: Text('Hovering: ${controller.hoverIndex.value}'),
onPressed: () {
Get.toNamed('/work/${widget.index}');
},
),
],
),
),
),
);
}
});
}
createTween(int hoverIndex, int index) {
TimelineTween timelineTween = TimelineTween<CardAnimationProps>();
var scene =
timelineTween.addScene(begin: 0.seconds, end: 2000.milliseconds);
scene.animate(
CardAnimationProps.isHoveredImgScale,
tween: Tween<double>(begin: 1, end: 1.3),
);
scene.animate(
CardAnimationProps.isHoveredTextOpacity,
tween: Tween<double>(begin: 0, end: 1),
);
scene.animate(
CardAnimationProps.isHoveredTextTranslateY,
tween: Tween<double>(begin: 20, end: 0),
);
return timelineTween;
}
createNotHoveredTween() {
TimelineTween timelineTween = TimelineTween<CardAnimationProps>();
var scene =
timelineTween.addScene(begin: 0.seconds, end: 2000.milliseconds);
scene.animate(
CardAnimationProps.isNotHoveredOpacity,
tween: Tween<double>(begin: 1, end: 0.2),
);
scene.animate(
CardAnimationProps.isNotHoveredTranslateX,
tween: Tween<double>(
begin: 0,
end: 20,
),
);
return timelineTween;
}
}
Controller code:
import 'package:get/get.dart';
class WorkController extends GetxController {
RxInt hoverIndex = (-1).obs;
@override
void onReady() {
super.onReady();
}
@override
void onClose() {}
}
But the animation is not smooth and it's just jumping from the states.
Any idea how this can made smoother or any other way this can be thought of implementing?