
import 'package:flutter/material.dart';
class ExplicitAnimationsScreen extends StatefulWidget {
const ExplicitAnimationsScreen({super.key});
State<ExplicitAnimationsScreen> createState() =>
_ExplicitAnimationsScreenState();
}
class _ExplicitAnimationsScreenState extends State<ExplicitAnimationsScreen>
with SingleTickerProviderStateMixin {
late final AnimationController _animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
reverseDuration: const Duration(seconds: 1),
);
late final Animation<Decoration> _decoration = DecorationTween(
begin: BoxDecoration(
color: Colors.amber,
borderRadius: BorderRadius.circular(20),
),
end: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(120),
),
).animate(_curve);
late final Animation<double> _rotation = Tween(
begin: 0.0,
end: 0.5,
).animate(_curve);
late final Animation<double> _scale = Tween(
begin: 1.0,
end: 1.1,
).animate(_curve);
late final Animation<Offset> _position = Tween(
begin: Offset.zero,
end: const Offset(0, -0.2),
).animate(_curve);
late final CurvedAnimation _curve = CurvedAnimation(
parent: _animationController,
curve: Curves.elasticOut,
reverseCurve: Curves.bounceIn,
);
void _play() {
_animationController.forward();
}
void _pause() {
_animationController.stop();
}
void _rewind() {
_animationController.reverse();
}
void dispose() {
_animationController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
print("build");
return Scaffold(
appBar: AppBar(
title: const Text('Explicit Animations'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SlideTransition(
position: _position,
child: ScaleTransition(
scale: _scale,
child: RotationTransition(
turns: _rotation,
child: DecoratedBoxTransition(
decoration: _decoration,
child: const SizedBox(
height: 400,
width: 400,
),
),
),
),
),
const SizedBox(
height: 50,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _play,
child: const Text("Play"),
),
ElevatedButton(
onPressed: _pause,
child: const Text("Pause"),
),
ElevatedButton(
onPressed: _rewind,
child: const Text("Rewind"),
)
],
)
],
),
),
);
}
}
late final AnimationController _animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
reverseDuration: const Duration(seconds: 1),
);
위 코드에서 _animationController와 관련된 다른 애니메이션 객체들은 _ExplicitAnimationsScreenState 객체가 완전히 생성된 후에야 초기화될 수 있습니다. SingleTickerProviderStateMixin을 사용하는 이 클래스는 this (즉, 클래스 자체의 인스턴스)를 vsync에 제공해야 하며, 이는 클래스 인스턴스가 완전히 생성된 후에만 가능합니다. 따라서, late 키워드는 이러한 종속적인 초기화를 가능하게 합니다.
이 코드에서 animate 메서드에 _curve를 인자로 사용하는 것은 애니메이션에 특정 "easing curve" (쉽게 말해, 애니메이션의 속도 곡선)를 적용하기 위함입니다. CurvedAnimation 객체는 애니메이션의 진행 방식을 조절합니다. 이는 애니메이션이 단순한 선형 또는 균일한 속도로 진행되는 대신, 더 자연스럽고 동적인 움직임을 표현할 수 있게 해줍니다.
속도 곡선 적용: CurvedAnimation은 부모 애니메이션(_animationController로부터 오는)의 진행률에 곡선을 적용합니다. 예를 들어, 시작할 때 느리고 끝날 때 빨라지거나, 반대로 시작할 때 빠르고 끝날 때 느려지는 효과를 줄 수 있습니다.
curve와 reverseCurve: _curve는 Curves.elasticOut으로 설정되어 있으며, 이는 애니메이션이 마지막에 탄력적인 효과를 가지고 멈추게 합니다. reverseCurve는 Curves.bounceIn으로, 애니메이션의 역방향 진행시 반듯이 바운스 효과를 나타냅니다.
Tween.animate: 여기서 Tween 객체들은 _decoration, _rotation, _scale, _position의 시작과 끝 값을 정의합니다. 그리고 animate 메서드를 통해 이 Tween들을 애니메이션 컨트롤러에 연결합니다.
_curve를 사용하는 이유: _curve를 animate 메서드에 전달함으로써, Tween에 정의된 값 변화에 속도 곡선을 적용합니다. 이는 애니메이션의 전체적인 움직임이나 변화에 더 다이나믹하고 자연스러운 느낌을 부여합니다.
예를 들어, _scale 애니메이션은 1.0 (원래 크기)에서 1.1 (10% 더 커진 크기)까지 변화합니다. _curve가 적용되면, 이 변화는 Curves.elasticOut에 정의된 대로 끝에서 탄력적인 효과를 가지며 진행됩니다. 사용자에게는 더욱 매력적이고 살아있는 듯한 인터랙션을 제공합니다.