2

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:

Expected smoothness

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?

Animation demo

  • Use AnimatedContainer instead of Card you will definitely get smoothness into your animation - like this answer https://stackoverflow.com/questions/71302456/flutter-use-hero-transition-between-custom-painter/71390601#71390601 – MohammedAli Mar 08 '22 at 07:16

0 Answers0