본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 10. 30. · 34 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)
함께 보면 좋은 카드 뉴스
Zipkin으로 추적 시각화 완벽 가이드
마이크로서비스 환경에서 분산 추적을 시각화하는 Zipkin의 핵심 개념과 활용 방법을 초급자도 쉽게 이해할 수 있도록 실무 스토리로 풀어낸 가이드입니다. Docker 실행부터 UI 분석까지 단계별로 배웁니다.
Spring AOT와 네이티브 이미지 완벽 가이드
Spring Boot 3.0부터 지원되는 AOT 컴파일과 GraalVM 네이티브 이미지를 통해 애플리케이션 시작 시간을 극적으로 단축하는 방법을 알아봅니다. 초급 개발자도 쉽게 이해할 수 있도록 실무 상황과 비유로 풀어냅니다.
Riverpod 3.0 쇼핑 앱 종합 프로젝트 완벽 가이드
Flutter와 Riverpod 3.0을 활용한 실무 수준의 쇼핑 앱 개발 과정을 단계별로 학습합니다. 상품 목록, 장바구니, 주문, 인증, 검색 기능까지 모든 핵심 기능을 구현하며 상태 관리의 실전 노하우를 익힙니다.
Riverpod 3.0 Retry 자동 재시도 완벽 가이드
Riverpod 3.0에 새로 추가된 Retry 기능을 활용하여 네트워크 오류나 일시적인 실패 상황에서 자동으로 재시도하는 방법을 배웁니다. 초급 개발자도 쉽게 따라할 수 있도록 실무 예제와 함께 설명합니다.
Riverpod 3.0 requireValue로 Provider 결합하기
Riverpod 3.0에 새로 추가된 requireValue를 활용하여 여러 Provider의 데이터를 효율적으로 결합하는 방법을 배웁니다. 비동기 데이터를 마치 동기 데이터처럼 다루는 실전 패턴을 소개합니다.