애니메이션이란?
애니메이션은 위젯의 속성 (위치, 크기, 색상 등등)을 시간에 따라 변경하여, 시각적인 변화를 부드럽게 보여주는 방식이다.
애니메이션은 사용자 경험을 향상시키는 중요한 요소로써, 사용자로 하여금 앱의 동작을 직관적으로 이해시키고, 몰입감을 높이는 데 사용된다.
결과적으로 UI를 움직이는 동작이기 때문에, StatefulWidget에서 사용된다.
애니메이션을 제어하는 속성들
애니메이션을 만들기에 앞서, 아래의 애니메이션들을 제어하는 속성들이 있다.
애니메이션 효과의 속도를 직접적으로 제어하는 Curve 와 애니메이션 시간을 제어하는 Duration 이다.
- Curve
애니메이션은 기본적으로 0 ~ 1 까지의 진행되는 값을 기반으로 작동한다.
Curve 객체는, 0 ~ 1 사이의 진행속도를 변화시키는 것을 제어한다.
- Duration
애니메이션이 진행되는 시간을 정의하는 객체이다.
애니메이션이 진행되는 0 ~ 1 을 진행하는데 걸리는 시간을 정의한다.
duration: const Duration(seconds: 2)
암시적 애니메이션 ( Implict Animation )
개발자가 직접 애니메이션의 세부 동작을 구현하지 않아도, 위젯의 특정 속성이 변경되면 자동으로 플러터가 애니메이션 효과를 적용시켜 주는 방식이다.
즉, 간단히 말하자면, 플러터에서 제공하는 애니메이션 위젯들이라고 생각하면 된다.
예시로는 다음과 같은 것들이 있다.
- AnimatedAlign
정렬 속성이 변경되면, 애니메이션이 진행되는 위젯
AnimatedAlign(
curve: Curves.easeIn,
alignment: selected ? Alignment.topRight : Alignment.bottomLeft,
duration: Duration(seconds: 1),
child: Container(
width: 50,
height: 50,
color: Colors.red,
),
),
- AnimatedContainer
컨테이너 속성이 변경되면, 애니메이션이 진행되는 위젯
AnimatedContainer(
alignment: selected ? Alignment.topRight : Alignment.bottomLeft,
height: selected ? 200 : 100,
width: selected ? 200 : 100,
decoration: BoxDecoration(
color: selected ? Colors.green : Colors.red,
borderRadius: BorderRadius.circular(
selected ? 0 : 20,
),
),
padding: EdgeInsets.all(selected ? 10 : 0),
curve: Curves.easeIn,
duration: Duration(seconds: 1),
child: Text('CHILD'),
)
- AnimatedPositioned
위치 속성이 변경되면, 애니메이션이 진행되는 위젯 (Stack에서 사용됨)
AnimatedPositioned(
left: selected ? 100 : 0,
top: selected ? 100 : 0,
curve: Curves.easeIn,
duration: Duration(seconds: 1),
child: Container(
width: 50,
height: 50,
color: Colors.amber,
),
)
등등 ( 추가적인 것들은 아래의 참고자료 (공식문서)) 참고)
Hero 위젯
화면이 이동될때 사용되는 애니메이션으로써, 같은 태그의 Hero 위젯들은 페이지가 이동되었을시, 마치 연결된 것처럼 화면 전환시 애니메이션 효과가 작동한다.
밑의 각 페이지의 Hero위젯들은, 'sample-image'라는 태그로 묶여있다.
이렇게 될 경우 A <-> B 로 페이지가 전환될때, Hero 위젯들은 화면전환 애니메이션 효과가 적용되어 보이게 된다!
// Page A
Hero(
tag: 'sample-image',
child: SizedBox(
width: 100,
height: 100,
child: Image.network(
'https://picsum.photos/200/200',
fit: BoxFit.cover,
),
),
)
// Page B
Hero(
tag: 'sample-image',
child: AspectRatio(
aspectRatio: 1,
child: Image.network(
'https://picsum.photos/200/200',
fit: BoxFit.cover,
),
),
)
명시적 애니메이션 ( Explicit Animation )
개발자가 애니메이션의 세부 동작을 직접 구현하는 방식이다.
AnimationController 객체로 애니메이션의 세부 동작을 제어할 수 있다.
이때 주의해야 하는것은, State에 vsync 속성이 들어가야 한다는 점이다.
일반적인 방법으로는 넣을 수 없고, State에 with 키워드를 통해 TickerProvider 객체를 이용해 주어야 한다.
TickerProvier 객체에는 크게 두가지의 종류가 있는데, 내부 설명은 생략하고, 간단하게 사용처만 말하면
SingleTickerProviderStateMixin 은 한 State에 애니메이션 하나만 사용할때 사용하고,
TickerProviderStateMixin은 한 State에 2개 이상의 애니메이션이 사용될때 사용하면 된다.
Tween 객체로는 애니메이션의 시작과 끝 값을 정의한다.
즉, 일반적인 AnimationController는 0 ~ 1 사이로 애니메이션의 시작과 끝을 정의하기에,
Tween과 AnimationController가 결합되면, 각각 0 ~ 1 과 Tween의 begin, end 가 각각 매칭되어 작동되게끔 한다.
// 이미지가 위, 아래로 조금씩만 반복적으로 움직이는 애니메이션
class ExamAnimation extends StatefulWidget {
const ExamAnimation({super.key});
@override
State<ExamAnimation> createState() => _ExamAnimationState();
}
class _ExamAnimationState extends State<ExamAnimation> with SingleTickerProviderStateMixin {
final AnimationController animationController;
final Animation<Offset> animation_offset;
final CurvedAnimation curve;
// animationController 정의, vsync와 duration 설정
animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
);
// animationController의 애니메이션 커브를 linear로 설정
curve =
CurvedAnimation(parent: animationController, curve: Curves.linear);
// 실제 애니메이션 정의
// Tween을 통해 begin에 Offset(0,-0.1), end에 Offset.zero를 매칭
// curve와 animationController 연결
animation_offset = Tween<Offset>(begin: const Offset(0, -0.1), end: Offset.zero)
.animate(curve);
@override
void initState(){
super.initState();
// 애니메이션 시작
animationController.forward();
// 애니메이션에 Listner 추가
fruitAnimationController.addStatusListener((state) {
// 애니메이션이 완료되었을 경우 reverse
if (state == AnimationStatus.completed) {
fruitAnimationController.reverse();
}
// 애니메이션이 초기상태이거나 시작되지 않았을 경우 forward
else if (state == AnimationStatus.dismissed) {
fruitAnimationController.forward();
}
});
}
@override
void dispose(){
super.dispose();
animationController.dispose();
}
@override
void build(){
return SlideTransition(position: animation_offset, child: Image.asset(assetImage)));
}
}
< 참고자료 >
Introduction to animations
How to perform animations in Flutter.
docs.flutter.dev
Animation and motion widgets
A catalog of Flutter's animation widgets.
docs.flutter.dev
'Flutter' 카테고리의 다른 글
(UX) NotificationListener 사용하기 - [ 무한 스크롤 , 당겨서 새로고침 ] (0) | 2024.12.19 |
---|---|
효율적인 이벤트 처리 기법 - [ 스로틀링 , 디바운싱 ] (0) | 2024.12.19 |
클린 아키텍처 심화 (0) | 2024.12.19 |
클린 아키텍쳐 와 추상화(다양화) (0) | 2024.12.09 |
Socket 통신, < 플러터 - STOMP 사용해보기 > (0) | 2024.12.02 |