본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 10. 30. · 63 Views
Flutter Animation 고급 패턴
Flutter 애니메이션의 고급 패턴, 성능 최적화, 물리 시뮬레이션을 완벽하게 마스터합니다
들어가며
이 글에서는 Flutter Animation 고급 패턴에 대해 상세히 알아보겠습니다. 총 14가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.
목차
- 복수_Controller_동기화_패턴
- ValueNotifier_성능_최적화
- CustomClipper_Reveal_애니메이션
- AnimateTo_정밀_제어_패턴
- Ticker_프레임_단위_제어
- 드래그_제스처_인터랙션
- PageRouteBuilder_커스텀_전환
- Matrix4_3D_Transform
- RepaintBoundary_격리_최적화
- Interval_타이밍_분할_패턴
- SpringSimulation_물리_시뮬레이션
- GravitySimulation_중력_물리
- Lerp_커스텀_보간_함수
- PathMetrics_경로_애니메이션
1. 복수 Controller 동기화 패턴
개요
여러 AnimationController를 동기화하여 복잡한 순차 애니메이션을 구현합니다.
코드 예제
class SyncedAnimations with TickerProviderStateMixin {
late AnimationController first;
late AnimationController second;
late AnimationController third;
void initControllers(TickerProvider vsync) {
first = AnimationController(
vsync: vsync,
duration: Duration(seconds: 1),
);
second = AnimationController(
vsync: vsync,
duration: Duration(milliseconds: 800),
);
third = AnimationController(
vsync: vsync,
duration: Duration(milliseconds: 600),
);
}
Future<void> playSequence() async {
await first.forward();
await Future.wait([
second.forward(),
third.forward(),
]);
await first.reverse();
}
}
설명
await으로 순차 실행, Future.wait으로 병렬 실행을 조합하여 정교한 애니메이션 시퀀스를 만듭니다.
2. ValueNotifier 성능 최적화
개요
ValueNotifier와 ValueListenableBuilder로 필요한 부분만 리빌드하여 성능을 극대화합니다.
코드 예제
class PerformantWidget extends StatefulWidget {
@override
_PerformantWidgetState createState() =>
_PerformantWidgetState();
}
class _PerformantWidgetState
extends State<PerformantWidget>
with SingleTickerProviderStateMixin {
late AnimationController _ctrl;
@override
void initState() {
super.initState();
_ctrl = AnimationController(
vsync: this,
duration: Duration(seconds: 3),
)..repeat(reverse: true);
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<double>(
valueListenable: _ctrl,
builder: (_, value, child) {
return Opacity(
opacity: value,
child: child,
);
},
child: ExpensiveStaticWidget(),
);
}
}
설명
child는 한 번만 빌드되고 value만 변경되어 성능이 크게 개선됩니다.
3. CustomClipper Reveal 애니메이션
개요
CustomClipper를 활용하여 복잡한 Reveal 효과를 구현합니다.
코드 예제
class RevealClipper extends CustomClipper<Path> {
final double revealPercent;
RevealClipper({required this.revealPercent});
@override
Path getClip(Size size) {
final path = Path();
final revealHeight = size.height * revealPercent;
path.addRect(Rect.fromLTWH(
0,
size.height - revealHeight,
size.width,
revealHeight,
));
return path;
}
@override
bool shouldReclip(RevealClipper old) =>
old.revealPercent != revealPercent;
}
// 사용
AnimatedBuilder(
animation: controller,
builder: (_, child) {
return ClipPath(
clipper: RevealClipper(
revealPercent: controller.value,
),
child: Image.asset('assets/image.png'),
);
},
)
설명
아래에서 위로 이미지가 서서히 드러나는 Reveal 효과를 만듭니다.
4. AnimateTo 정밀 제어 패턴
개요
animateTo 메서드로 애니메이션의 목표값과 속도를 동적으로 제어합니다.
코드 예제
late AnimationController _ctrl;
void _animateToTarget(double target) {
_ctrl.animateTo(
target,
duration: Duration(milliseconds: 800),
curve: Curves.easeInOutCubic,
);
}
// 상태 리스너로 자동 반복
_ctrl.addStatusListener((status) {
if (status == AnimationStatus.completed) {
_ctrl.animateTo(
0.0,
duration: Duration(seconds: 1),
curve: Curves.easeOut,
);
} else if (status == AnimationStatus.dismissed) {
_ctrl.animateTo(
1.0,
duration: Duration(seconds: 1),
curve: Curves.easeIn,
);
}
});
설명
forward/reverse 대신 animateTo로 특정 값까지만 애니메이션하거나 방향을 동적으로 변경할 수 있습니다.
5. Ticker 프레임 단위 제어
개요
Ticker를 직접 사용하여 프레임 단위로 애니메이션을 제어합니다.
코드 예제
class TickerBasedWidget extends StatefulWidget {
@override
_TickerBasedWidgetState createState() =>
_TickerBasedWidgetState();
}
class _TickerBasedWidgetState
extends State<TickerBasedWidget>
with SingleTickerProviderStateMixin {
late Ticker _ticker;
double _angle = 0.0;
@override
void initState() {
super.initState();
_ticker = createTicker((elapsed) {
setState(() {
_angle = (elapsed.inMilliseconds / 1000) * pi;
});
});
_ticker.start();
}
@override
void dispose() {
_ticker.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Transform.rotate(
angle: _angle,
child: FlutterLogo(size: 100),
);
}
}
설명
Ticker는 프레임마다 elapsed 시간을 제공하여 시간 기반의 정밀한 애니메이션이 가능합니다.
6. 드래그 제스처 인터랙션
개요
사용자의 드래그 제스처에 반응하는 인터랙티브 애니메이션을 구현합니다.
코드 예제
class DraggableBox extends StatefulWidget {
@override
_DraggableBoxState createState() =>
_DraggableBoxState();
}
class _DraggableBoxState extends State<DraggableBox>
with SingleTickerProviderStateMixin {
Offset _offset = Offset.zero;
late AnimationController _snapCtrl;
@override
void initState() {
super.initState();
_snapCtrl = AnimationController(
vsync: this,
duration: Duration(milliseconds: 400),
);
}
void _onPanEnd(DragEndDetails details) {
final animation = Tween<Offset>(
begin: _offset,
end: Offset.zero,
).animate(CurvedAnimation(
parent: _snapCtrl,
curve: Curves.elasticOut,
));
_snapCtrl.forward(from: 0).then((_) {
setState(() => _offset = Offset.zero);
});
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanUpdate: (details) {
setState(() {
_offset += details.delta;
});
},
onPanEnd: _onPanEnd,
child: Transform.translate(
offset: _offset,
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
),
);
}
}
설명
드래그 중에는 즉시 반응하고, 놓으면 elasticOut으로 원점으로 돌아갑니다.
7. PageRouteBuilder 커스텀 전환
개요
PageRouteBuilder로 페이지 전환 애니메이션을 완전히 커스터마이징합니다.
코드 예제
class FadeSlidePageRoute extends PageRouteBuilder {
final Widget page;
FadeSlidePageRoute({required this.page})
: super(
pageBuilder: (context, animation, secondaryAnimation)
=> page,
transitionsBuilder: (
context,
animation,
secondaryAnimation,
child,
) {
const begin = Offset(0.0, 0.3);
const end = Offset.zero;
final slideTween = Tween(begin: begin, end: end);
final fadeTween = Tween<double>(begin: 0.0, end: 1.0);
final slideAnimation = animation.drive(
slideTween.chain(
CurveTween(curve: Curves.easeOut),
),
);
final fadeAnimation = animation.drive(fadeTween);
return SlideTransition(
position: slideAnimation,
child: FadeTransition(
opacity: fadeAnimation,
child: child,
),
);
},
);
}
// 사용
Navigator.push(
context,
FadeSlidePageRoute(page: NextPage()),
);
설명
슬라이드와 페이드를 결합한 부드러운 페이지 전환을 만듭니다.
8. Matrix4 3D Transform
개요
Matrix4로 3D 회전과 원근 효과를 구현합니다.
코드 예제
AnimatedBuilder(
animation: controller,
builder: (context, child) {
final angle = controller.value * pi * 2;
return Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.002)
..rotateX(angle * 0.3)
..rotateY(angle),
alignment: Alignment.center,
child: Container(
width: 200,
height: 300,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.purple],
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black38,
blurRadius: 20,
offset: Offset(0, 10),
),
],
),
),
);
},
)
설명
setEntry(3, 2, 0.002)로 원근감을 주고 X, Y축 동시 회전으로 3D 카드 효과를 만듭니다.
9. RepaintBoundary 격리 최적화
개요
RepaintBoundary로 리페인트 영역을 격리하여 성능을 최적화합니다.
코드 예제
Column(
children: [
RepaintBoundary(
child: AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Transform.rotate(
angle: controller.value * 2 * pi,
child: FlutterLogo(size: 100),
);
},
),
),
SizedBox(height: 20),
ExpensiveStaticList(),
],
)
설명
애니메이션 위젯만 격리하여 나머지 UI는 리페인트되지 않아 성능이 향상됩니다.
10. Interval 타이밍 분할 패턴
개요
Interval로 하나의 controller를 시간대별로 분할하여 순차 애니메이션을 만듭니다.
코드 예제
final firstHalf = CurvedAnimation(
parent: controller,
curve: Interval(0.0, 0.5, curve: Curves.easeIn),
);
final secondHalf = CurvedAnimation(
parent: controller,
curve: Interval(0.5, 1.0, curve: Curves.easeOut),
);
Column(
children: [
ScaleTransition(
scale: firstHalf,
child: Box(color: Colors.red),
),
SizedBox(height: 20),
SlideTransition(
position: Tween<Offset>(
begin: Offset(1, 0),
end: Offset.zero,
).animate(secondHalf),
child: Box(color: Colors.blue),
),
],
)
설명
첫 번째는 00.5초, 두 번째는 0.51초에 애니메이션되어 순차적으로 실행됩니다.
11. SpringSimulation 물리 시뮬레이션
개요
스프링 물리 법칙을 시뮬레이션하여 자연스러운 애니메이션을 만듭니다.
코드 예제
void _runSpringAnimation() {
final spring = SpringSimulation(
SpringDescription(
mass: 1.0,
stiffness: 200.0,
damping: 15.0,
),
controller.value,
1.0,
0.0,
);
controller.animateWith(spring);
}
// 또는 내장 Curve 사용
final springCurve = SpringCurve();
final animation = CurvedAnimation(
parent: controller,
curve: springCurve,
);
설명
mass(질량), stiffness(강성), damping(감쇠)로 스프링의 물리적 특성을 조절합니다.
12. GravitySimulation 중력 물리
개요
중력 시뮬레이션으로 자유낙하 효과를 구현합니다.
코드 예제
void _dropWithGravity() {
final gravity = GravitySimulation(
9.8,
controller.value,
1.0,
0.0,
);
controller.animateWith(gravity);
}
// 바운스 효과 추가
controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
final bounce = SpringSimulation(
SpringDescription.withDampingRatio(
mass: 1.0,
stiffness: 300.0,
),
1.0,
0.8,
-5.0,
);
controller.animateWith(bounce);
}
});
설명
GravitySimulation으로 낙하하고, 바닥에 닿으면 SpringSimulation으로 튕겨 올라갑니다.
13. Lerp 커스텀 보간 함수
개요
커스텀 lerp 함수로 복잡한 값의 보간을 구현합니다.
코드 예제
class GradientTween extends Tween<Gradient> {
GradientTween({Gradient? begin, Gradient? end})
: super(begin: begin, end: end);
@override
Gradient lerp(double t) {
if (begin is LinearGradient && end is LinearGradient) {
final b = begin as LinearGradient;
final e = end as LinearGradient;
return LinearGradient(
colors: List.generate(
b.colors.length,
(i) => Color.lerp(
b.colors[i],
e.colors[i],
t,
)!,
),
);
}
return end!;
}
}
// 사용
final gradientAnimation = GradientTween(
begin: LinearGradient(
colors: [Colors.blue, Colors.purple],
),
end: LinearGradient(
colors: [Colors.red, Colors.orange],
),
).animate(controller);
설명
Gradient처럼 복잡한 타입도 lerp 함수를 구현하여 부드럽게 보간할 수 있습니다.
14. PathMetrics 경로 애니메이션
개요
PathMetrics로 복잡한 경로를 따라 움직이는 애니메이션을 구현합니다.
코드 예제
class PathAnimationPainter extends CustomPainter {
final double progress;
final Path path;
PathAnimationPainter({
required this.progress,
required this.path,
});
@override
void paint(Canvas canvas, Size size) {
final metrics = path.computeMetrics().first;
final distance = metrics.length * progress;
final tangent = metrics.getTangentForOffset(distance);
if (tangent != null) {
canvas.drawCircle(
tangent.position,
10,
Paint()..color = Colors.red,
);
}
}
@override
bool shouldRepaint(PathAnimationPainter old) =>
old.progress != progress;
}
설명
computeMetrics로 경로의 길이를 계산하고, getTangentForOffset으로 특정 지점의 위치를 얻어 경로를 따라 움직입니다.
마치며
이번 글에서는 Flutter Animation 고급 패턴에 대해 알아보았습니다. 총 14가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.
관련 태그
#Flutter #Animation #Advanced #Performance #Physics
댓글 (0)
함께 보면 좋은 카드 뉴스
AAA급 게임 프로젝트 완벽 가이드
Flutter와 Flame 엔진을 활용하여 AAA급 퀄리티의 모바일 게임을 개발하는 전체 과정을 다룹니다. 기획부터 앱 스토어 출시까지, 실무에서 필요한 모든 단계를 이북처럼 술술 읽히는 스타일로 설명합니다.
빌드와 배포 자동화 완벽 가이드
Flutter 앱 개발에서 GitHub Actions를 활용한 CI/CD 파이프라인 구축부터 앱 스토어 자동 배포까지, 초급 개발자도 쉽게 따라할 수 있는 빌드 자동화의 모든 것을 다룹니다.
게임 분석과 메트릭스 완벽 가이드
Flutter와 Flame으로 개발한 게임의 성공을 측정하고 개선하는 방법을 배웁니다. Firebase Analytics 연동부터 A/B 테스팅, 리텐션 분석까지 데이터 기반 게임 운영의 모든 것을 다룹니다.
Reusable Voice Clone Prompt 완벽 가이드
TTS 음성 복제 API에서 반복되는 프롬프트 계산을 제거하고 캐싱 전략을 활용하여 대량 음성 생성 성능을 극대화하는 방법을 다룹니다. 초급 개발자도 쉽게 따라할 수 있는 실전 최적화 기법을 소개합니다.
게임 보안과 치팅 방지 완벽 가이드
Flutter와 Flame 게임 엔진에서 클라이언트 보안부터 서버 검증까지, 치터들로부터 게임을 보호하는 핵심 기법을 다룹니다. 초급 개발자도 쉽게 따라할 수 있는 실전 보안 코드와 함께 설명합니다.