Animasyonlar, bir uygulamayi siradandan muhtesemin ayiran en onemli unsurdur. Kullanicilar animasyonlu bir uygulamayi kullandiginda, dokunuslarinin karsilik buldugunu hissederler. Flutter, animasyon konusunda inanilmaz guclu bir altyapi sunuyor — implicit widget'lardan custom painter'a, hero animasyonlardan staggered transition'lara kadar her seyi kapsiyor.
Bu rehberde Flutter animasyon dunyasinin derinliklerine dalacagiz. Basit fade-in efektlerinden karmasik fizik tabanli animasyonlara kadar her seviyeyi ele alacagiz.
Not: Tum ornekler Flutter 3.x ile test edilmistir. Performans olcumleri release mode'da yapilmistir.
Icindekiler
- Animasyon Temelleri
- Implicit Animasyonlar
- Explicit Animasyonlar
- Hero Animasyonlari
- Staggered Animasyonlar
- CustomPainter ile Animasyon
- Fizik Tabanli Animasyonlar
- Rive ve Lottie Entegrasyonu
- Performans Optimizasyonu
- Best Practices
- Sonuc ve Oneriler
1. Animasyon Temelleri
Flutter'da animasyonlar iki ana kategoriye ayrilir:
Animasyon Tipi Karsilastirmasi
Tip | Kontrol | Kullanim Alani | Zorluk |
|---|---|---|---|
**Implicit** | Otomatik | Basit gecisler | Kolay |
**Explicit** | AnimationController | Karmasik sekanslar | Orta |
**Hero** | Framework yonetimli | Sayfa gecisleri | Kolay |
**Staggered** | Interval + Controller | Siralama animasyonlar | Zor |
**Physics-based** | Simulation | Dogal hareket | Orta |
**Custom Paint** | Canvas API | Ozel cizimler | Zor |
**Rive/Lottie** | Dis arac | Tasarimci ciktilari | Kolay |
Temel Kavramlar
dart
1// AnimationController: Tum explicit animasyonlarin kalbi2class _MyWidgetState extends State 3 with SingleTickerProviderStateMixin {4 late final AnimationController _controller;5 late final Animation _fadeAnimation; 6 late final Animation _slideAnimation; 7 8 @override9 void initState() {10 super.initState();11 _controller = AnimationController(12 duration: const Duration(milliseconds: 800),13 vsync: this, // SingleTickerProviderStateMixin gerektirir14 );15 16 _fadeAnimation = Tween( 17 begin: 0.0,18 end: 1.0,19 ).animate(CurvedAnimation(20 parent: _controller,21 curve: Curves.easeInOut,22 ));23 24 _slideAnimation = Tween( 25 begin: const Offset(0, 0.3),26 end: Offset.zero,27 ).animate(CurvedAnimation(28 parent: _controller,29 curve: Curves.easeOutCubic,30 ));31 32 _controller.forward();33 }34 35 @override36 void dispose() {37 _controller.dispose(); // Memory leak onlemek icin sart!38 super.dispose();39 }40 41 @override42 Widget build(BuildContext context) {43 return FadeTransition(44 opacity: _fadeAnimation,45 child: SlideTransition(46 position: _slideAnimation,47 child: const Card(child: Text('Animasyonlu Kart')),48 ),49 );50 }51}2. Implicit Animasyonlar
Implicit animasyonlar, Flutter'in en kolay animasyon yontemidir. Sadece degeri degistirirsiniz, Flutter gecisi otomatik yapar.
dart
1class AnimatedButtonExample extends StatefulWidget {2 const AnimatedButtonExample({super.key});3 4 @override5 State createState() => _AnimatedButtonExampleState(); 6}7 8class _AnimatedButtonExampleState extends State { 9 bool _isExpanded = false;10 bool _isHighlighted = false;11 12 @override13 Widget build(BuildContext context) {14 return GestureDetector(15 onTap: () => setState(() => _isExpanded = !_isExpanded),16 onTapDown: (_) => setState(() => _isHighlighted = true),17 onTapUp: (_) => setState(() => _isHighlighted = false),18 onTapCancel: () => setState(() => _isHighlighted = false),19 child: AnimatedContainer(20 duration: const Duration(milliseconds: 400),21 curve: Curves.easeOutBack,22 width: _isExpanded ? 280 : 160,23 height: _isExpanded ? 80 : 56,24 decoration: BoxDecoration(25 color: _isHighlighted ? Colors.blue.shade700 : Colors.blue,26 borderRadius: BorderRadius.circular(_isExpanded ? 20 : 28),27 boxShadow: [28 BoxShadow(29 color: Colors.blue.withOpacity(_isExpanded ? 0.4 : 0.2),30 blurRadius: _isExpanded ? 20 : 8,31 offset: const Offset(0, 4),32 ),33 ],34 ),35 child: Center(36 child: AnimatedDefaultTextStyle(37 duration: const Duration(milliseconds: 300),38 style: TextStyle(39 color: Colors.white,40 fontSize: _isExpanded ? 20 : 16,41 fontWeight: FontWeight.bold,42 ),43 child: Text(_isExpanded ? 'Genisletildi!' : 'Tikla'),44 ),45 ),46 ),47 );48 }49}Kullanilabilir Implicit Widget'lar
- AnimatedContainer: — boyut, renk, padding, margin, decoration
- AnimatedOpacity: — saydamlik gecisi
- AnimatedPositioned: — Stack icerisinde konum degisimi
- AnimatedCrossFade: — iki widget arasinda gecis
- AnimatedSwitcher: — widget degisiminde animasyon
- AnimatedAlign: — hizalama gecisi
- AnimatedPadding: — padding gecisi
3. Explicit Animasyonlar
Daha fazla kontrol gerektiginde explicit animasyonlar kullanilir:
dart
1class PulsingDotIndicator extends StatefulWidget {2 final int dotCount;3 const PulsingDotIndicator({super.key, this.dotCount = 3});4 5 @override6 State createState() => _PulsingDotIndicatorState(); 7}8 9class _PulsingDotIndicatorState extends State 10 with SingleTickerProviderStateMixin {11 late final AnimationController _controller;12 13 @override14 void initState() {15 super.initState();16 _controller = AnimationController(17 duration: const Duration(milliseconds: 1500),18 vsync: this,19 )..repeat();20 }21 22 @override23 void dispose() {24 _controller.dispose();25 super.dispose();26 }27 28 @override29 Widget build(BuildContext context) {30 return Row(31 mainAxisSize: MainAxisSize.min,32 children: List.generate(widget.dotCount, (index) {33 return AnimatedBuilder(34 animation: _controller,35 builder: (context, child) {36 final delay = index * 0.2;37 final t = (_controller.value - delay).clamp(0.0, 1.0);38 final scale = 1.0 + 0.5 * Curves.easeInOut.transform(39 t < 0.5 ? t * 2 : 2 - t * 2,40 );41 return Transform.scale(42 scale: scale,43 child: Container(44 margin: const EdgeInsets.symmetric(horizontal: 4),45 width: 12,46 height: 12,47 decoration: BoxDecoration(48 color: Colors.blue.withOpacity(0.5 + 0.5 * scale / 1.5),49 shape: BoxShape.circle,50 ),51 ),52 );53 },54 );55 }),56 );57 }58}Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
4. Hero Animasyonlari
Hero animasyonlari, iki sayfa arasinda paylasilir bir widget'in akici gecisini saglar:
dart
1// Liste sayfasi2class ProductListPage extends StatelessWidget {3 const ProductListPage({super.key});4 5 @override6 Widget build(BuildContext context) {7 return ListView.builder(8 itemCount: products.length,9 itemBuilder: (context, index) {10 final product = products[index];11 return GestureDetector(12 onTap: () {13 Navigator.push(14 context,15 MaterialPageRoute(16 builder: (_) => ProductDetailPage(product: product),17 ),18 );19 },20 child: Hero(21 tag: 'product-image-${product.id}',22 child: ClipRRect(23 borderRadius: BorderRadius.circular(12),24 child: Image.network(25 product.imageUrl,26 height: 120,27 width: 120,28 fit: BoxFit.cover,29 ),30 ),31 ),32 );33 },34 );35 }36}37 38// Detay sayfasi39class ProductDetailPage extends StatelessWidget {40 final Product product;41 const ProductDetailPage({super.key, required this.product});42 43 @override44 Widget build(BuildContext context) {45 return Scaffold(46 body: Column(47 children: [48 Hero(49 tag: 'product-image-${product.id}',50 child: Image.network(51 product.imageUrl,52 height: 300,53 width: double.infinity,54 fit: BoxFit.cover,55 ),56 ),57 Padding(58 padding: const EdgeInsets.all(16),59 child: Text(product.name, style: const TextStyle(fontSize: 24)),60 ),61 ],62 ),63 );64 }65}5. Staggered Animasyonlar
Birden fazla elementin sirasyla animasyonu, profesyonel gorunum saglar:
dart
1class StaggeredListAnimation extends StatefulWidget {2 const StaggeredListAnimation({super.key});3 4 @override5 State createState() => _StaggeredListAnimationState(); 6}7 8class _StaggeredListAnimationState extends State 9 with SingleTickerProviderStateMixin {10 late final AnimationController _controller;11 final List items = ['Profil', 'Ayarlar', 'Bildirimler', 'Yardim', 'Cikis']; 12 13 @override14 void initState() {15 super.initState();16 _controller = AnimationController(17 duration: const Duration(milliseconds: 1200),18 vsync: this,19 )..forward();20 }21 22 @override23 void dispose() {24 _controller.dispose();25 super.dispose();26 }27 28 @override29 Widget build(BuildContext context) {30 return ListView.builder(31 itemCount: items.length,32 itemBuilder: (context, index) {33 final startInterval = index * 0.1;34 final endInterval = startInterval + 0.4;35 36 final slideAnimation = Tween( 37 begin: const Offset(-1, 0),38 end: Offset.zero,39 ).animate(CurvedAnimation(40 parent: _controller,41 curve: Interval(42 startInterval.clamp(0.0, 1.0),43 endInterval.clamp(0.0, 1.0),44 curve: Curves.easeOutCubic,45 ),46 ));47 48 final fadeAnimation = Tween( 49 begin: 0.0,50 end: 1.0,51 ).animate(CurvedAnimation(52 parent: _controller,53 curve: Interval(54 startInterval.clamp(0.0, 1.0),55 endInterval.clamp(0.0, 1.0),56 ),57 ));58 59 return SlideTransition(60 position: slideAnimation,61 child: FadeTransition(62 opacity: fadeAnimation,63 child: ListTile(64 leading: const Icon(Icons.arrow_forward),65 title: Text(items[index]),66 ),67 ),68 );69 },70 );71 }72}6. CustomPainter ile Animasyon
Canvas uzerinde ozel cizimler yapmak icin CustomPainter kullanilir:
dart
1class WaveAnimation extends StatefulWidget {2 const WaveAnimation({super.key});3 4 @override5 State createState() => _WaveAnimationState(); 6}7 8class _WaveAnimationState extends State 9 with SingleTickerProviderStateMixin {10 late final AnimationController _controller;11 12 @override13 void initState() {14 super.initState();15 _controller = AnimationController(16 duration: const Duration(seconds: 3),17 vsync: this,18 )..repeat();19 }20 21 @override22 void dispose() {23 _controller.dispose();24 super.dispose();25 }26 27 @override28 Widget build(BuildContext context) {29 return AnimatedBuilder(30 animation: _controller,31 builder: (context, child) {32 return CustomPaint(33 size: const Size(double.infinity, 200),34 painter: WavePainter(progress: _controller.value),35 );36 },37 );38 }39}40 41class WavePainter extends CustomPainter {42 final double progress;43 WavePainter({required this.progress});44 45 @override46 void paint(Canvas canvas, Size size) {47 final paint = Paint()48 ..color = Colors.blue.withOpacity(0.6)49 ..style = PaintingStyle.fill;50 51 final path = Path();52 path.moveTo(0, size.height);53 54 for (double x = 0; x <= size.width; x++) {55 final y = size.height * 0.5 +56 20 * sin((x / size.width * 2 * pi) + (progress * 2 * pi)) +57 10 * sin((x / size.width * 4 * pi) + (progress * 4 * pi));58 path.lineTo(x, y);59 }60 61 path.lineTo(size.width, size.height);62 path.close();63 canvas.drawPath(path, paint);64 }65 66 @override67 bool shouldRepaint(WavePainter oldDelegate) =>68 oldDelegate.progress != progress;69}7. Fizik Tabanli Animasyonlar
dart
1// Spring animasyonu2class SpringButton extends StatefulWidget {3 const SpringButton({super.key});4 5 @override6 State createState() => _SpringButtonState(); 7}8 9class _SpringButtonState extends State 10 with SingleTickerProviderStateMixin {11 late final AnimationController _controller;12 late Animation _scaleAnimation; 13 14 @override15 void initState() {16 super.initState();17 _controller = AnimationController(vsync: this);18 _scaleAnimation = _controller.drive(Tween(begin: 1.0, end: 0.85));19 }20 21 void _onTapDown(TapDownDetails details) {22 _controller.animateWith(23 SpringSimulation(24 const SpringDescription(mass: 1, stiffness: 300, damping: 15),25 0, 1, 0,26 ),27 );28 }29 30 void _onTapUp(TapUpDetails details) {31 _controller.animateWith(32 SpringSimulation(33 const SpringDescription(mass: 1, stiffness: 300, damping: 15),34 1, 0, 0,35 ),36 );37 }38 39 @override40 void dispose() {41 _controller.dispose();42 super.dispose();43 }44 45 @override46 Widget build(BuildContext context) {47 return GestureDetector(48 onTapDown: _onTapDown,49 onTapUp: _onTapUp,50 child: ScaleTransition(51 scale: _scaleAnimation,52 child: Container(53 padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),54 decoration: BoxDecoration(55 color: Colors.deepPurple,56 borderRadius: BorderRadius.circular(16),57 ),58 child: const Text('Spring Buton',59 style: TextStyle(color: Colors.white, fontSize: 18)),60 ),61 ),62 );63 }64}8. Rive ve Lottie Entegrasyonu
Tasarimci araclarindan gelen animasyonlar icin Rive ve Lottie kullanilir:
dart
1// Lottie kullanimi2import 'package:lottie/lottie.dart';3 4class LottieExample extends StatelessWidget {5 const LottieExample({super.key});6 7 @override8 Widget build(BuildContext context) {9 return Lottie.asset(10 'assets/animations/loading.json',11 width: 200,12 height: 200,13 fit: BoxFit.contain,14 repeat: true,15 );16 }17}18 19// Rive kullanimi20import 'package:rive/rive.dart';21 22class RiveExample extends StatelessWidget {23 const RiveExample({super.key});24 25 @override26 Widget build(BuildContext context) {27 return const SizedBox(28 width: 300,29 height: 300,30 child: RiveAnimation.asset(31 'assets/animations/character.riv',32 fit: BoxFit.contain,33 stateMachines: ['StateMachine'],34 ),35 );36 }37}9. Performans Optimizasyonu
Animasyon Performans Kontrol Listesi
Teknik | Etki | Zorluk |
|---|---|---|
**RepaintBoundary** | Yuksek | Kolay |
**const widget** kullanimi | Orta | Kolay |
**AnimatedBuilder** tercih edin | Yuksek | Orta |
**Opacity yerine FadeTransition** | Orta | Kolay |
**shouldRepaint dogru implement** | Yuksek | Orta |
**Image cache** kullanin | Yuksek | Kolay |
dart
1// KOTU: Tum widget tree her frame'de rebuild olur2AnimatedBuilder(3 animation: _controller,4 builder: (context, child) {5 return Opacity(6 opacity: _controller.value,7 child: ExpensiveWidget(), // Her frame'de yeniden olusturulur!8 );9 },10);11 12// IYI: child parametresi ile optimize13AnimatedBuilder(14 animation: _controller,15 child: const ExpensiveWidget(), // Bir kez olusturulur16 builder: (context, child) {17 return FadeTransition(18 opacity: _controller,19 child: child, // Cache'lenmis child kullanilir20 );21 },22);10. Best Practices
- Her zaman dispose metodunda controller'i temizleyin
- Mumkunse implicit animasyon tercih edin
- RepaintBoundary: ile gereksiz repaint'leri onleyin
- Profile mode: ile animasyon performansini olcun
- Animasyon surelerini 200-500ms araliginda tutun
Sonuç ve Öneriler
Flutter'in animasyon altyapisi inanilmaz guclu ve esnek. Implicit animasyonlardan explicit controller'lara, Rive entegrasyonundan custom painter animasyonlarina kadar genis bir yelpaze sunuyor. Dogru araci dogru yerde kullanarak 60fps akiciliginda, goz alici animasyonlar olusturabilirsiniz. Onemli olan, her animasyonun bir amacinin olmasi — geri bildirim vermek, dikkat cekmek veya gecis saglamak.
Animasyon gelistirirken performansi her zaman on planda tutun. RepaintBoundary ile gereksiz repaint'leri onleyin, AnimationController'lari dispose etmeyi unutmayin ve profile mode ile performansi duzenli olarak olcun. Animasyon surelerini 200-500ms araliginda tutmak, kullanicilarin en rahat hissettigi aralik olarak kanitlanmistir. Ayrica prefers-reduced-motion ayarina saygi gostermek, erisilebirlik acisindan kritiktir.
Son olarak, "less is more" prensibini benimsemenizi oneririz. Kullanicilar bir uygulamada cok fazla animasyon gordugunde dikkat dagitici bulurlar. flutter_animate paketi ve Rive gibi araclarla profesyonel animasyonlari hizla prototipleyebilir, ardindan kullanici testleriyle hangilerinin gercekten deger kattigini olcebilirsiniz.
ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.
Okuyucu Ödülü
Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var:

