Custom Flutter Animations with the Animation Controller
[Ko]
- flutter animation의 3대 요소 : Ticker, Animation Controller, Animation value
- Ticker : 매 틱 단위마다 애니메이션이 1frame씩 움직이게 됨. ticker는 매 tick마다 새로운 set state를 실행해서 위젯의 각 상태 단계별 rendering을 하게 됨.
- Animation controller : animation controller의 property에 animation value를 설정함으로써 animation 대상에게 start, stop, go forward, loop back, animation period, height, size, color, alpha, opacity 등의 설정 명령을 내림.
- Animation controller의 vsync property에는 TickerProvider를 값으로 설정해야 함. Ticker provider는 일반적으로 state object가 되며, this로 표시함.
- state object에 with를 이용해서 SingleTickerProviderStateMixin을 적용시켜 줌으로써 TickerProvider로 기능하도록 해 줌.(single animation에 대한 ticker provider 기능을 제공하는 state 관리 mixin)
- animation이 2개 이상인 경우는 TickerProviderStateMixin을 사용.
- Mixin은 class에 새로운 기능을 넣어주며, 한번에 여러개의 mixin을 적용할 수 있음. extends(상속)은 1개만 적용 가능.
- controller는 기본적으로 숫자를 단계별로 증가시킴. duration을 1초로 설정하면 1초 동안 60회의 단계로 ticker가 작동함. controller.forward()는 0~1까지 60 단계로 숫자를 증가시키는 방향.
- 실제 애니메이션은 controller.addListener(실행 fuction;);를 이용해서 설정. addListener 내의 실행 함수로 빈 setState를 넣어주어야 매 tick마다 변경되는 controller.value 값이 animation 상태에 반영됨.
- controller.value가 0~1사이에서 단계적으로 변하게 되므로 이 값을 animation 설정에 사용함.
return Scaffold(
backgroundColor: Colors.red.withOpacity(controller.value),
- 로딩 인디케이터로서 활용 : ${controller.value.toInt()}%'
- CurvedAnimation widget을 이용하면 0~1의 증가 패턴을 linear 외의 다른 패턴으로 변경할 수 있음.
- CurvedAnimation을 사용할 때는 upperBound가 1보다 크면 안 됨. 1보다 크면 app crash 발생.
- Curves.decelerate : 처음에는 빠르고 나중에 느림
- Curves.easeIn : 처음에는 느리고 나중에 빠름
- add.StatusListner를 이용하면 애이메이션 바운스 효과를 줄 수 있음.
animation.addStatusListener((status) {
print(status);
if (status == AnimationStatus.completed) {
controller.reverse(from: 1.0);
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
- tween animation : tween animation은 여러 종류가 있음. 색상에 적용되는 것은 ColorTween.
animation = ColorTween(begin: Colors.blueGrey, end: Colors.white,).animate(controller);
- 애니메니션을 사용할 때는 반드시 dispose method를 overwrite해서 controller가 dispose 되도록 해야 함. Dispose 처리를 하지 않으면 다른 화면으로 넘어가서 더이상 애니메이션이 필요없는 상태에서도 controller가 살아있게 되어 memory leak 가 발생함.
- final code
import 'package:flutter/material.dart';
import 'login_screen.dart';
import 'registration_screen.dart';
class WelcomeScreen extends StatefulWidget {
static const String id = 'welcome_screen';
@override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation animation;
@override
void initState() {
// TODO: implement initState
super.initState();
controller = AnimationController(
duration: Duration(seconds: 3),
vsync: this,
// upperBound: 100.0,
);
animation = CurvedAnimation(parent: controller, curve: Curves.decelerate);
controller.forward();
// controller.reverse(from:1.0);
animation.addStatusListener((status) {
print(status);
if (status == AnimationStatus.completed) {
controller.reverse(from: 1.0);
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
controller.addListener(() {
setState(() {});
// print(controller.value);
print(animation.value);
});
}
[En]
- Three major elements of flutter animation: Ticker, Animation Controller, Animation value
- Ticker: Animation moves 1 frame every tick unit. The ticker executes a new set state at every tick, rendering each stage of the widget.
- Animation controller: By setting the animation value in the properties of the animation controller, a command such as start, stop, go forward, loop back, animation period, height, size, color, alpha, opacity is given to the animation target.
- TickerProvider must be set as a value in vsync property of Animation controller. Ticker provider is generally a state object and is marked as this.
- By applying SingleTickerProviderStateMixin by using with on state object, it functions as TickerProvider. (state management mixin that provides ticker provider function for single animation)
- TickerProviderStateMixin is used when there are two or more animations.
- Mixin adds a new function to the class, and multiple mixins can be applied at once. Only one extends can be applied.
- The controller basically increases the number step by step. If duration is set to 1 second, ticker operates in 60 steps for 1 second. controller.forward () increases the number from 0 to 1 in 60 steps.
- The actual animation is set using controller.addListener (run fuction;) ;. When an empty setState is entered as an execution function in addListener, the controller.value changed every tick is reflected in the animation state.
- Since the controller.value changes step by step between 0 and 1, this value is used for animation setting.
return Scaffold(
backgroundColor: Colors.red.withOpacity(controller.value),
- Used as loading indicator: $ {controller.value.toInt ()}% '
- If you use the CurvedAnimation widget, you can change the increase pattern from 0 to 1 to a pattern other than linear.
- When using CurvedAnimation, upperBound should not be greater than 1. If it is greater than 1, an app crash occurs.
- Curves.decelerate: fast at first and slow later
- Curves.easeIn: Slow at first and faster at later
- By using add.StatusListner, you can give an animation bounce effect.
animation.addStatusListener((status) {
print(status);
if (status == AnimationStatus.completed) {
controller.reverse(from: 1.0);
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
- Tween animation: There are several types of tween animation. The color applied to ColorTween.
animation = ColorTween(begin: Colors.blueGrey, end: Colors.white,).animate(controller);
- When using animation, be sure to overwrite the dispose method to dispose the controller. If dispose is not performed, the controller will remain alive even if animation is no longer needed because it moves to another screen and a memory leak occurs.
- final code
import 'package:flutter/material.dart';
import 'login_screen.dart';
import 'registration_screen.dart';
class WelcomeScreen extends StatefulWidget {
static const String id = 'welcome_screen';
@override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation animation;
@override
void initState() {
// TODO: implement initState
super.initState();
controller = AnimationController(
duration: Duration(seconds: 3),
vsync: this,
// upperBound: 100.0,
);
animation = CurvedAnimation(parent: controller, curve: Curves.decelerate);
controller.forward();
// controller.reverse(from:1.0);
animation.addStatusListener((status) {
print(status);
if (status == AnimationStatus.completed) {
controller.reverse(from: 1.0);
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
controller.addListener(() {
setState(() {});
// print(controller.value);
print(animation.value);
});
}