mix icon indicating copy to clipboard operation
mix copied to clipboard

Animations as attribute

Open tilucasoli opened this issue 1 year ago • 3 comments

Package version

1.0.0-beta.1

Flutter version

Flutter 3.16.8 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 67457e669f (10 days ago) • 2024-01-16 16:22:29 -0800
Engine • revision 6e2ea58a5c
Tools • Dart 3.2.5 • DevTools 2.28.5

Steps to reproduce

I tried to create a animation that changes the background color to another over a specific duration when the user hovers the widget. To do this, I used an AnimatedBox as shown in the code below:

Pressable(
  child: AnimatedBox(
    duration: const Duration(milliseconds: 300),
    style: Style(
      height(50),
      width(50),
      backgroundColor.red(),
      onHover(
        backgroundColor.blue(),
      ),
    ),
  ),
)

Perfect! This code works really well. to make this animation avaliable to other widgets. I developed an attribute to handle it using WidgetDecorator, as shown below::

class ColorTransitionOnHoverDecorator
    extends WidgetDecorator<ColorTransitionOnHoverDecorator> {
  final Color initialColor;
  final Color finalColor;
  final Duration duration;

  const ColorTransitionOnHoverDecorator(
    this.initialColor,
    this.finalColor, {
    required this.duration,
    super.key,
  });

  @override
  ColorTransitionOnHoverDecorator lerp(VisibilityDecorator? other, double t) {
    return ColorTransitionOnHoverDecorator(initialColor, finalColor,
        duration: Duration.zero);
  }

  @override
  get props => [initialColor];

  @override
  Widget build(mix, child) => AnimatedBox(
        duration: duration,
        style: Style(
          box.color(initialColor),
          onHover(
            box.color(finalColor),
          ),
        ),
        child: child,
      );
}

class ColorTransitionOnHoverUtility<T extends StyleAttribute>
    extends MixUtility<T, ColorTransitionOnHoverDecorator> {
  const ColorTransitionOnHoverUtility(super.builder);

  T call(
    Color initialColor,
    Color finalColor, {
    required Duration duration,
    Key? key,
  }) =>
      builder(ColorTransitionOnHoverDecorator(
        initialColor,
        finalColor,
        duration: duration,
        key: key,
      ));
}

final colorTransitionOnHover = ColorTransitionOnHoverUtility((d) => d);

Therefore, I replace the AnimatedBox with the new attribute colorTransitionOnHover:


Pressable(
  child: Box(
    style: Style(
      height(50),
      width(50),
      colorTransitionOnHover(
        Colors.red,
        Colors.blue,
        duration: const Duration(milliseconds: 300),
      ),
    ),
  ),
)

It's also work well! However, when I applied the same attribute colorTransitionOnHover to an HBox, it didn't work. The HBox didn't receive any Flex attributes.

Pressable(
  child: HBox(
    style: Style(
      flex.mainAxisSize.min(),
      flex.crossAxisAlignment.center(),
      flex.mainAxisAlignment.end(),
      padding(10),
      colorTransitionOnHover(
        Colors.blueAccent,
        Colors.redAccent,
        duration: const Duration(milliseconds: 300),
      ),
    ),
    children: [
      Box(
        style: Style(
          height(50),
          width(50),
          backgroundColor.black(),
        ),
      ),
      Box(
        style: Style(
          height(50),
          width(50),
          backgroundColor.white(),
        ),
      )
    ],
  ),
);

Expected results

https://github.com/conceptadev/mix/assets/62367544/d8907e4d-3e60-4f7d-bd5b-e05e92e34483

Actual results

https://github.com/conceptadev/mix/assets/62367544/1f6409bc-f0b8-4bc9-bee3-1a955072b11a

Code sample

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: WidgetAnimatedWithAttribute(),
        ),
      ),
    );
  }
}

class WidgetAnimatedWithoutAttribute extends StatelessWidget {
  const WidgetAnimatedWithoutAttribute({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Pressable(
      child: AnimatedBox(
        duration: const Duration(milliseconds: 300),
        style: Style(
          box.color(Colors.blueAccent),
          onHover(
            box.color(Colors.redAccent),
          ),
        ),
        child: HBox(
          style: Style(
            flex.mainAxisSize.min(),
            flex.crossAxisAlignment.center(),
            flex.mainAxisAlignment.end(),
            padding(10),
          ),
          children: [
            Box(
              style: Style(
                height(50),
                width(50),
                backgroundColor.black(),
              ),
            ),
            Box(
              style: Style(
                height(50),
                width(50),
                backgroundColor.white(),
              ),
            )
          ],
          // duration: const Duration(milliseconds: 300),
        ),
      ),
    );
  }
}

class WidgetAnimatedWithAttribute extends StatelessWidget {
  const WidgetAnimatedWithAttribute({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Pressable(
      child: HBox(
        style: Style(
          flex.mainAxisSize.min(),
          flex.crossAxisAlignment.center(),
          flex.mainAxisAlignment.end(),
          padding(10),
          colorTransitionOnHover(
            Colors.blueAccent,
            Colors.redAccent,
            duration: const Duration(milliseconds: 300),
          ),
        ),
        children: [
          Box(
            style: Style(
              height(50),
              width(50),
              backgroundColor.black(),
            ),
          ),
          Box(
            style: Style(
              height(50),
              width(50),
              backgroundColor.white(),
            ),
          )
        ],
        // duration: const Duration(milliseconds: 300),
      ),
    );
  }
}

class ColorTransitionOnHoverDecorator
    extends WidgetDecorator<ColorTransitionOnHoverDecorator> {
  final Color initialColor;
  final Color finalColor;
  final Duration duration;

  const ColorTransitionOnHoverDecorator(
    this.initialColor,
    this.finalColor, {
    required this.duration,
    super.key,
  });

  @override
  ColorTransitionOnHoverDecorator lerp(VisibilityDecorator? other, double t) {
    return ColorTransitionOnHoverDecorator(initialColor, finalColor,
        duration: Duration.zero);
  }

  @override
  get props => [initialColor];

  @override
  Widget build(mix, child) => AnimatedBox(
        duration: duration,
        style: Style(
          box.color(initialColor),
          onHover(
            box.color(finalColor),
          ),
        ),
        child: child,
      );
}

class ColorTransitionOnHoverUtility<T extends StyleAttribute>
    extends MixUtility<T, ColorTransitionOnHoverDecorator> {
  const ColorTransitionOnHoverUtility(super.builder);

  T call(
    Color initialColor,
    Color finalColor, {
    required Duration duration,
    Key? key,
  }) =>
      builder(ColorTransitionOnHoverDecorator(
        initialColor,
        finalColor,
        duration: duration,
        key: key,
      ));
}

final colorTransitionOnHover = ColorTransitionOnHoverUtility((d) => d);

tilucasoli avatar Jan 26 '24 17:01 tilucasoli