Tüm Yazılar
KategoriFlutter
Okuma Süresi
26 dk okuma
Yayın Tarihi
...
Kelime Sayısı
1.995kelime

Kahveni hazırla - bu içerikli bir makale!

Flutter ile implicit, explicit, hero, staggered ve custom painter animasyonlari. Rive, Lottie entegrasyonu ve 60fps performans teknikleri.

Flutter Ileri Seviye Animasyonlar: Detayli Rehber

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


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 kalbi
2class _MyWidgetState extends State
3 with SingleTickerProviderStateMixin {
4 late final AnimationController _controller;
5 late final Animation _fadeAnimation;
6 late final Animation _slideAnimation;
7 
8 @override
9 void initState() {
10 super.initState();
11 _controller = AnimationController(
12 duration: const Duration(milliseconds: 800),
13 vsync: this, // SingleTickerProviderStateMixin gerektirir
14 );
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 @override
36 void dispose() {
37 _controller.dispose(); // Memory leak onlemek icin sart!
38 super.dispose();
39 }
40 
41 @override
42 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 @override
5 State createState() => _AnimatedButtonExampleState();
6}
7 
8class _AnimatedButtonExampleState extends State {
9 bool _isExpanded = false;
10 bool _isHighlighted = false;
11 
12 @override
13 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 @override
6 State createState() => _PulsingDotIndicatorState();
7}
8 
9class _PulsingDotIndicatorState extends State
10 with SingleTickerProviderStateMixin {
11 late final AnimationController _controller;
12 
13 @override
14 void initState() {
15 super.initState();
16 _controller = AnimationController(
17 duration: const Duration(milliseconds: 1500),
18 vsync: this,
19 )..repeat();
20 }
21 
22 @override
23 void dispose() {
24 _controller.dispose();
25 super.dispose();
26 }
27 
28 @override
29 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 sayfasi
2class ProductListPage extends StatelessWidget {
3 const ProductListPage({super.key});
4 
5 @override
6 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 sayfasi
39class ProductDetailPage extends StatelessWidget {
40 final Product product;
41 const ProductDetailPage({super.key, required this.product});
42 
43 @override
44 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 @override
5 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 @override
14 void initState() {
15 super.initState();
16 _controller = AnimationController(
17 duration: const Duration(milliseconds: 1200),
18 vsync: this,
19 )..forward();
20 }
21 
22 @override
23 void dispose() {
24 _controller.dispose();
25 super.dispose();
26 }
27 
28 @override
29 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 @override
5 State createState() => _WaveAnimationState();
6}
7 
8class _WaveAnimationState extends State
9 with SingleTickerProviderStateMixin {
10 late final AnimationController _controller;
11 
12 @override
13 void initState() {
14 super.initState();
15 _controller = AnimationController(
16 duration: const Duration(seconds: 3),
17 vsync: this,
18 )..repeat();
19 }
20 
21 @override
22 void dispose() {
23 _controller.dispose();
24 super.dispose();
25 }
26 
27 @override
28 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 @override
46 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 @override
67 bool shouldRepaint(WavePainter oldDelegate) =>
68 oldDelegate.progress != progress;
69}

7. Fizik Tabanli Animasyonlar

dart
1// Spring animasyonu
2class SpringButton extends StatefulWidget {
3 const SpringButton({super.key});
4 
5 @override
6 State createState() => _SpringButtonState();
7}
8 
9class _SpringButtonState extends State
10 with SingleTickerProviderStateMixin {
11 late final AnimationController _controller;
12 late Animation _scaleAnimation;
13 
14 @override
15 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 @override
40 void dispose() {
41 _controller.dispose();
42 super.dispose();
43 }
44 
45 @override
46 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 kullanimi
2import 'package:lottie/lottie.dart';
3 
4class LottieExample extends StatelessWidget {
5 const LottieExample({super.key});
6 
7 @override
8 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 kullanimi
20import 'package:rive/rive.dart';
21 
22class RiveExample extends StatelessWidget {
23 const RiveExample({super.key});
24 
25 @override
26 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 olur
2AnimatedBuilder(
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 optimize
13AnimatedBuilder(
14 animation: _controller,
15 child: const ExpensiveWidget(), // Bir kez olusturulur
16 builder: (context, child) {
17 return FadeTransition(
18 opacity: _controller,
19 child: child, // Cache'lenmis child kullanilir
20 );
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:

Etiketler

#Flutter#Animation#Dart#UI#Motion#Rive#Lottie
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

Bunu da begenebilirsiniz