본문으로 바로가기

Hero

(https://flutter.dev/docs/development/ui/animations/hero-animations)

 

Hero 위젯으로 감싸고 동일한 태그를 주면 페이지 이동시 애니메이션이 적용된다. 반드시 하나만 연결할 필요는 없고 여러군데 연결하는 것도 가능하다.

Hero(
  tag: 'logo',
child: Container(
  height: 200.0,
  child: Image.asset('images/logo.png'),
  ),
),
Hero(
  tag: 'logo',
  child: Container(
    height: 200.0,
    child: Image.asset('images/logo.png'),
  ),
),

 

 

Custom Animation (linear)

 

커스텀 애니메이션을 사용하기 위한 3가지 개념이 있다.

 

- ticker

State 위젯은 ticker로 기능할 수 있다.

SingleTickerProviderStateMixin 애니메이션 1

TickerProviderStateMixin 여러 애니메이션

 

- AnimationController

AnimationController -> vsyncduration, upperBound 등을 통해 애니메이션을 통제할 수 있음.

 

- controller.value

controller.value를 통해 변하는 값을 특정 속성에 넣음으로써 애니메이션을 구현할 수 있음.

 

class WelcomeScreen extends StatefulWidget {
  static const id = 'welcome';
  @override
  _WelcomeScreenState createState() => _WelcomeScreenState();
}

// State 위젯에 애니메이션을 설정해야 합니다.
// with SingleTickerProviderStateMixin을 통해 애니메이션 1개를 담당하는 mixin 생성
class _WelcomeScreenState extends State<WelcomeScreen> with SingleTickerProviderStateMixin {
    
  // 컨트롤러 선언
  AnimationController controller;

  // 애니메이션 적용을 위해 initState에 설정
  @override
  void initState() {
    super.initState();

    // AnimationController로 애니메이션 컨트롤러 지정. vsync는 현 클래스(this)
    // lowerBound와 upperBound는 controller.value(ticker)의 크기를 말합니다. 지정하지 않으면 0.0 ~ 1.0입니다.
    // 지금은 0.0 부터 100.0 까지를 1초 동안 움직입니다.
    controller = AnimationController(
        vsync: this, duration: Duration(seconds: 1), upperBound: 100.0);

    // ticker를 앞으로(+) 진행하라는 말.
    // controller.reverse(from: 100.0) 와 같이 여러 메서드가 있습니다.
    controller.forward();
    
    controller.addListener(() {
      // setState를 주지 않으면 애니메이션이 적용되지 않습니다.
      setState(() {});
      print(controller.value);
    });
  }

 

위 컨트롤러에 따르면 controller.value 는 0.0~100.0 까지의 수치를 1초간 이동합니다. (linear하게요)

이 값을 다음과 같이 활용해서 애니메이션을 구현할 수 있습니다.

Text(
  '${controller.value}%',
  style: TextStyle(
    fontSize: 45.0,
    fontWeight: FontWeight.w900,
  ),
),
SizedBox(
  height: controller.value,
),

 

 

Custom Animation (curved)

 

위와 같은 애니메이션은 기본적으로 linear하게 움직입니다. curved하게 움직이고 싶다면 위의 애니메이션이 일부만 수정해주면 됩니다.

 

Curves class (https://api.flutter.dev/flutter/animation/Curves-class.html)

CurvedAnimation (https://api.flutter.dev/flutter/animation/CurvedAnimation-class.html)

 

 

class WelcomeScreen extends StatefulWidget {
  static const id = 'welcome';

  @override
  _WelcomeScreenState createState() => _WelcomeScreenState();
}

class _WelcomeScreenState extends State<WelcomeScreen> with SingleTickerProviderStateMixin {
  
  // AnimationController 변수를 하나 만듭니다.
  AnimationController controller;
  
  // Animation 변수를 하나 만듭니다.
  Animation animation;
  
  @override
  void initState() {
    super.initState();

    // 컨트롤러를 등록합니다 (주의! upperBound를 주면 안됩니다. curved는 0.0~1.0에서만 움직입니다)
    controller =
        AnimationController(vsync: this, duration: Duration(seconds: 1));

    // animation에 CurvedAnimation을 생성합니다.
    // parent는 controller를 등록하고, curve는 Curves 클래스를 사용합니다. (첨부링크 참고)
    animation = CurvedAnimation(parent: controller, curve: Curves.decelerate);

    // 컨트롤러를 1.0부터 0.0으로 감소시킵니다.
    controller.reverse(from: 1.0)

    controller.addListener(() {
      setState(() {});
      // controller.value가 아닌 animation.value를 사용합니다.
      print(animation.value);
    });
  }

 

0.0 ~ 1.0에서만 움직이므로 활용시 숫자를 곱해서 적절하게 활용합시다.

Hero(
   tag: 'logo',
   child: Container(
       child: Image.asset('images/logo.png'),
       height: animation.value * 100,
   ),
),

 

 

무한 애니메이션

 

애니메이션이 한 번 작동한 후 계속 작동하게 하고 싶을 때 활용하는 addStatusListener를 활용한 trick이 있다. 애니메이션이 작동을 완료하면 status를 반환하는데  controller가 움직이는 방식이 forward면 AnimationStatus.completed이고, reverse면 AnimationStatus.dismissed이다.

 

이를 이용해 반환하는 status가 completed면 reverse를 돌리고, reverse가 끝나면 dismissed을 반환할 테니 다시 forward를 돌리면 무한히 움직이게 된다.

 

  @override
  void initState() {
    super.initState();

    controller =
        AnimationController(vsync: this, duration: Duration(seconds: 1));

    animation = CurvedAnimation(parent: controller, curve: Curves.easeInOut);

    controller.forward();

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

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

 

여기서 주의할 점은 무한 애니메이션이 적용된 페이지를 벗어나도 백그라운드에서 해당 애니메이션이 계속 움직이고 있다는 것입니다. 따라서, 해당 페이지를 벗어날 때는 (stful 위젯의 lifecycle에 따른) dispose를 통해 해다 애니메이션을 제거해줄 필요가 있습니다. 

 

controller.dispose() 메서드를 통해 정지시킵니다.

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

 

 


 

etc tip)

 

위의 animation으로 바꿀 수 없는 값들은 Tween 클래스를 이용하면 바꿀 수 있습니다.

(https://api.flutter.dev/flutter/animation/Tween-class.html)

 

예를 들어 색깔을 바꾸고 싶다면 ColorTween을 사용합시다.

(https://api.flutter.dev/flutter/animation/ColorTween-class.html)

 

animation = ColorTween(begin: Colors.red, end: Colors.blue)
        .animate(CurvedAnimation(parent: controller, curve: Curves.easeInOut));

 


 

수작업을 통해 애니메이션을 구현할 수도 있지만 남이 구현해 놓은 패키지를 설치하여 활용할 수도 있습니다. 검색을 통해 사용하고 싶은 것을 알아서 찾아 공부하고 활용하는 것이 필요하겠지만 

 

animated_text_kit가 점수가 인지도가 높은 편이기 때문에 첨부합니다. (멋있습니다)

 

(https://pub.dev/packages/animated_text_kit)

 

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체