简体   繁体   中英

Flutter Custom Animated Icon

I am looking to make a item that gets added and removed from list.

what I am looking for is to have the + icon and the - icon and animate between the 2 for a clean and smooth look.

I have the following code

Container(
            padding: EdgeInsets.fromLTRB(0, 0, 20, 0),
            child: AnimatedIcon(
              icon: AnimatedIcons.home_menu,
              progress: _animationController,
              color: Colors.white,
            ))

with

void _handleOnPressed() {
setState(() {
  isPlaying = !isPlaying;
  isPlaying
      ? _animationController.forward()
      : _animationController.reverse();
  });
}

This for fine for the built in animated icons in flutter.

I am looking to use the + and the - icon, but with the same look.

Is there a way to do this?

You're in luck my friend! Flutter already has you covered with AnimatedIcon()

AnimatedIcon Class in the docs

Animated Icon Widget of the week Video

Now to animate your Icons with Flare. Jeff Delaney made a good tutorial for this.

https://fireship.io/lessons/animated-navigation-flutter-flare/

  class _CreatePackageViewState extends State<CreatePackageView>
with SingleTickerProviderStateMixin {  
   bool expanded = true;
  AnimationController controller;
  @override
   void initState() {
    super.initState();
    controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 400),
      reverseDuration: Duration(milliseconds: 400),
    );
  }



  IconButton(
        icon: AnimatedIcon(
          icon: AnimatedIcons.menu_close,
          progress: controller,
          semanticLabel: 'Show menu',
        ),
        onPressed: () {
          setState(() {
            expanded ? controller.forward() : controller.reverse();
            expanded = !expanded;
          });
        }),

}

You need 3 things for custom animations

  1. Animation Controller
  2. Tween
  3. Animation

Simple example below

import 'package:flutter/material.dart';

class AnimationsPractice extends StatefulWidget {
  @override
  _AnimationsPracticeState createState() => _AnimationsPracticeState();
}

class _AnimationsPracticeState extends State<AnimationsPractice> with SingleTickerProviderStateMixin {

  AnimationController controller;
  Animation animation;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
        duration: Duration(milliseconds: 500),
        vsync: this);

   animation = ColorTween(begin: Colors.purple, end: Colors.red[700]).animate(controller);

    controller.forward();

    animation.addStatusListener((status) {
      if(status == AnimationStatus.completed){
        controller.reverse(from: 1.0);
        print(animation.value);
      }
      else if(status == AnimationStatus.dismissed){
        controller.forward();
      }
    });

    controller.addListener(() {
      setState(() {
      });
      print(controller.value);
    });
  }

  @override
  void dispose() {
    super.dispose();
    controller.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.yellow,
      appBar: AppBar(
        title: Text('Heart Beat'),
      ),
      body: Center(
        child: Icon(
          Icons.favorite,
          size: animation.value * 100,
        ),
      ),
    );
  }
}

I know it's not as beautiful as AnimatedIcon , but you could actually get very similar transition with any 2 icons of your choice with just a few lines of code:

 IconButton(
      icon: AnimatedSwitcher(
          duration: const Duration(milliseconds: 300),
          transitionBuilder: (child, anim) => RotationTransition(
                turns: child.key == ValueKey('icon1')
                    ? Tween<double>(begin: 1, end: 0.75).animate(anim)
                    : Tween<double>(begin: 0.75, end: 1).animate(anim),
                child: FadeTransition(opacity: anim, child: child),
              ),
          child: _currIndex == 0
              ? Icon(Icons.close, key: const ValueKey('icon1'))
              : Icon(
                  Icons.arrow_back,
                  key: const ValueKey('icon2'),
                )),
      onPressed: () {
        setState(() {
          _currIndex = _currIndex == 0 ? 1 : 0;
        });
      },
    );

Result: 结果


Or you can use ScaleTransition instead of FadeTransition , and get even more similar animation:

IconButton(
      icon: AnimatedSwitcher(
          duration: const Duration(milliseconds: 350),
          transitionBuilder: (child, anim) => RotationTransition(
                turns: child.key == ValueKey('icon1')
                    ? Tween<double>(begin: 1, end: 0.75).animate(anim)
                    : Tween<double>(begin: 0.75, end: 1).animate(anim),
                child: ScaleTransition(scale: anim, child: child),
              ),
          child: _currIndex == 0
              ? Icon(Icons.close, key: const ValueKey('icon1'))
              : Icon(
                  Icons.arrow_back,
                  key: const ValueKey('icon2'),
                )),
      onPressed: () {
        setState(() {
          _currIndex = _currIndex == 0 ? 1 : 0;
        });
      },
    )

Result: 结果-2


With this approach you could use any icons you want, and it doesn't require creating separate AnimationController just to control the transition, unlike AnimatedIcon

It looks like the Flutter team use a command line tool called vitool to convert SVGs to paths that can be consumed as an animated icon. If you have a relatively simple animated SVG, you can try running the main.dart command line tool within the bin folder of this repository:https://github.com/flutter/flutter/tree/master/dev/tools/vitool . I haven't attempted yet, but I will try and report back if I find any issues. The output should look like the following:

const _AnimatedIconData _$menu_arrow = _AnimatedIconData(
  Size(48.0, 48.0),
  <_PathFrames>[
    _PathFrames(
      opacities: <double>[
        1.0,
        1.0,
        1.0,
        1.0,
        1.0,
        1.0,
        1.0,
(...)

you can simply use animate_icons 2.0.0 package to animate two icons

AnimateIcons(
    startIcon: Icons.add_circle,
    endIcon: Icons.add_circle_outline,
    size: 100.0,
    controller: controller,
    // add this tooltip for the start icon
    startTooltip: 'Icons.add_circle',
    // add this tooltip for the end icon
    endTooltip: 'Icons.add_circle_outline',
    size: 60.0,
    onStartIconPress: () {
        print("Clicked on Add Icon");
        return true;
    },
    onEndIconPress: () {
        print("Clicked on Close Icon");
        return true;
    },
    duration: Duration(milliseconds: 500),
    startIconColor: Colors.deepPurple,
    endIconColor: Colors.deepOrange,
    clockwise: false,
),

https://pub.dev/packages/animate_icons

参考图片

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM