Flutter IO 2022 Extended ‘애니메이션 적용기’

valas(김선호)
Flutter Seoul
Published in
11 min readJul 22, 2022

Flutter I/O Extended Korea 2022에서 발표한 “애니메이션 적용기” 정리글 입니다.

1. 배우게 될 것

FooTransition(Slide, Fade, Rotation, Size) 애니메이션 사용법

2. 기본 필수 개념

좌표계와 애니메이션의 종류, 구현에 필요한 AnimationController, Tween, Curve, SingleTickerProviderMixin 가 필요합니다.

좌표계

  • 왼쪽 상단을 기준 0,0
  • 오른쪽 하단이 화면의 넓이와 높이

애니메이션 종류

플러터 공식 애니메이션 가이드 입니다.

제가 정리해본 애니메이션의 종류 입니다.

다음으로 코드 구현에 필요한 클래스 입니다.

AnimationController, Tween, Curve, SingleTickerProviderMixin

AnimationController

class _Page2State extends State<Page2> with 
SingleTickerProviderStateMixin {
late final AnimationController controller ;
@override
void initState() {
controller= AnimationController(vsync: this, duration: const Duration(seconds: 1));
}

주로 사용하는 AnimationController의 메소드입니다.

  • controller.forward();
  • controller.reverse();
  • controller.repeat();
  • controller.stop();
  • controller.reset();

가 있고, 리스너의 콜백을 받는 addStatusListener 도 자주 사용 됩니다.

  • controller.addStatusListener();

Tween, Curve

애니메이션의 시작과 끝의 효과 입니다. 주로 메소드 체이닝으로 animate() 를 호출해 사용됩니다. animate()를 호출하면, Animation 이 리턴되는데 플러터에서 관리하는 Animation의 진행 상태를 나타내는 값 입니다. Curve를 받아 진행 속도를 커스텀 할 수 있습니다.

Tween<T>(begin: 시작 효과의 상대적 값, end: 끝 효과의 상대적 값).animate(CurvedAnimation(parent:controller,curve: Curves.ease))

SizeTransition에서 위젯의 width가 200, height가 100인 경우

//크기가 0에서 width 200, height 100으로 커짐 
Tween<double>(begin:0.0, end: 1.0)
//width 200, height 100에서 작아지다 없어짐
Tween<double>(begin:1.0, end: 0.0)

FadeTransition에서

//사라진 상태에서 나타남 (fade in)
Tween<double>(begin:0.0, end: 1.0)
//사라짐
Tween<double>(begin:1.0, end: 0.0)

RotationTransition에서

//90도 회전 
Tween<double>(begin:0.0, end: 0.25)
//180도 회전
Tween<double>(begin:0.0, end: 0.5)

SlideTransition에서 위젯의 width가 200, height가 100인 경우

//화면 오른쪽으로 200, 아래로 100만큼 이동 
Tween<Offset>(begin:Offset(0,0), end: Offset(1,1)
//화면 왼쪽으로 200, 위로 100만큼 이동
Tween<Offset>(begin:Offset(0,0), end: Offset(-1,-1)
//화면 왼쪽으로 100, 아래로 50만큼 이동
Tween<Offset>(begin:Offset(0,0), end: Offset(-0.5,0.5)

StepTween<int>

단계 별로 애니메이션이 필요할 때 사용. 예를 들어 텍스트가 1개씩 나타나는 애니메이션이 필요할 때 사용합니다.

StepTween(begin: 0, end: text.length)

SingleTickerProviderStateMixin

애니메이션 구현시 타이머 역할을 하는 클래스. 매 프레임마다 Ticker를 호출하는 객체. 여러개의 AnimationController가 필요한 경우 TickerProviderStateMixin 사용합니다.

3. 구현

공통으로 사용하는 위젯이나 속성을 찾고, 파일하나에서 공통으로 사용하도록 관리합니다.

  • 주요 색깔 : 연한 파랑,짙은 파랑, 빨강
  • 글씨 폰트 : 제목, 본문, 헤더, 달력
  • 공통 컴포넌트 : 달력모양

순서

  1. 디자인만 구현
  2. 차례대로 애니메이션을 적용

2페이지에만 있는 애니메이션만 설명하면 4가지 모두 설명됩니다. 다른 애니메이션은 깃헙 코드 참고해주세요.

  • 배경 더 연한 파란색 FadeInTransition
  • 컵의 SlideTransition + RotationTransition
  • Search for 텍스트 위젯의 SizeTransition or StepTween 사용

AnimationController 선언

class _Page2State extends State<Page2> with SingleTickerProviderStateMixin {
late final AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(duration: const Duration(seconds: 3), vsync: this);
controller.forward();
}

컵 SlidTransition + RotationTransition + FadeTransition위젯

Animation<Offset> cupSlideUpAnimation = 
Tween(
begin: 0.0, end: 0.07).animate(
CurvedAnimation(
parent: controller,
curve: const ShakeCurve(count: 3)));
Animation<double> cupRotateAnimation =
Tween(
begin: Offset(0, 1), end: Offset(0, 0)).animate(
CurvedAnimation(
parent: controller,
curve: Curves.elasticOut));
Animation<double> cupFadeInAnimation =
Tween(
begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Curves.easeOutCubic));

적용

FooTransition은 중첩 적용이 가능합니다.

@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: cupFadeInAnimation,
child: SlideTransition(
position: cupSlideUpAnimation,
child: RotationTransition(turns: cupRotateAnimation, child: child),
),
);
}

Search for… 텍스트 위젯

SizeTransition과 StepTween 2가지 방법으로 구현 가능합니다.

SizeTransition 사용

속성을 잘 보면 , axis로 크기가 변하는 방향, axisAlignment로 시작 위치를 지정합니다.

@override
Widget build(BuildContext context) {
Animation<double> sizeFactorTween =
Tween<double>(begin: 0, end: 1).animate(controller);
return Positioned(
top: 150,
left: 20,
child: SizeTransition(
sizeFactor: sizeFactorTween,
axis: Axis.horizontal, //수평방향으로
axisAlignment: -1, //위젯 왼쪽에서
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(appBarSearchText,
style: const TextStyle(
fontSize: 22,
color: Colors.white,
fontWeight: FontWeight.bold))),
),
);
}

StepTween 으로 구현

late Animation<int> textStepAnimation;
final String appBarSearchText = "Search for ...";
@override
void initState() {
textStepAnimation = StepTween(begin: 0, end: appBarSearchText.length)
.animate(CurvedAnimation(parent: controller, curve: Curves.easeIn));
}
...
child: AnimatedBuilder(
builder: (context, child) {
String text =
appBarSearchText.substring(0, textStepAnimation.value);
return Text(text,
style: const TextStyle(
fontSize: 22,
color: Colors.white,
fontWeight: FontWeight.bold));
},
animation: textStepAnimation,
),

4. 마무리

적용된 화면 입니다.

위에서 설명한 애니메이션은 아래 깃헙에서 전체 코드 확인할 수 있습니다. 애니메이션 구현, 쉽습니다. 찡긋
https://github.com/larsien/UIChallenge_event_ios_mobile_app_animation_by_ronasit

https://github.com/larsien/UIChallenge_event_ios_mobile_app_animation_by_ronasit2

5. reference

--

--