Bölüm 8 — Konu 36: Explicit Animasyonlar (`AnimationController`, `Tween`, `Curve`)
Dizi · 36/61 Flutter Türkçe Tutorial
- Bölüm 1 — Konu 1: Flutter Nedir, Mimarisi (Widget → Element → RenderObject, Skia/Impeller)
- Bölüm 1 — Konu 2: Ortam Kurulumu (Flutter SDK, Android Studio/VS Code, Emulator/Simulator, DevTools'a İlk Bakış)
- Bölüm 1 — Konu 3: İlk Proje Yapısı (`pubspec.yaml`, `lib/`, Klasör Mimarisi, `flutter create` Anatomisi)
- Bölüm 1 — Konu 4: Hot Reload / Hot Restart Ne Yapıyor, Neden Önemli
- Bölüm 2 — Konu 5: "Her Şey Widget'tır" Felsefesi, Widget Ağacı
- Bölüm 2 — Konu 6: StatelessWidget vs StatefulWidget + `setState()` Derinlemesine (Rebuild Mekanizması)
- Bölüm 2 — Konu 7: Temel Layout — Container, Row, Column, Stack, Padding, Align, Center
- Bölüm 2 — Konu 8: Constraint Sistemi — "Constraints Go Down, Sizes Go Up, Parent Sets Position"
- Bölüm 2 — Konu 9: `Expanded`, `Flexible`, `Spacer`, Intrinsic Widget'lar
- Bölüm 3 — Konu 10: MaterialApp/CupertinoApp, Scaffold, AppBar
- Bölüm 3 — Konu 11: `ListView`, `GridView`, `SingleChildScrollView` (+ Builder Pattern)
- Bölüm 3 — Konu 12: Text, TextStyle, Icon, Image, Asset Yönetimi
- Bölüm 3 — Konu 13: Custom Widget Yazma Prensipleri (Composition Over Inheritance)
- Bölüm 3 — Konu 14: Navigator 1.0 — Push/Pop, Named Routes
- Bölüm 4 — Konu 15: Form, TextField/TextFormField, GlobalKey<FormState>, Validasyon
- Bölüm 4 — Konu 16: Theme Sistemi (ThemeData, ColorScheme)
- Bölüm 4 — Konu 17: Responsive & Adaptive Tasarım (MediaQuery, LayoutBuilder, OrientationBuilder, Breakpoint Stratejileri)
- Bölüm 5 — Konu 18: Future/Async-Await Flutter Bağlamında, FutureBuilder
- Bölüm 5 — Konu 19: `Stream`, `StreamBuilder`
- Bölüm 5 — Konu 20: HTTP İstekleri (`http` / `dio` Paketleri)
- Bölüm 5 — Konu 21: JSON Serialization (Manuel → `json_serializable`/`freezed`)
- Bölüm 5 — Konu 22: Local Storage — `shared_preferences` → `Hive` → `sqflite`/Drift
- Bölüm 6 — Konu 23: InheritedWidget ve InheritedModel — "Neden setState Yetmiyor" Sorusunun Cevabı
- Bölüm 6 — Konu 24: Provider Paketi
- Bölüm 6 — Konu 25: Riverpod (Modern Yaklaşım, Provider'ın Halefi)
- Bölüm 6 — Konu 26: BLoC/Cubit Pattern (`flutter_bloc`)
- Bölüm 6 — Konu 27: GetX (Tartışmalı Ama Yaygın)
- Bölüm 6 — Konu 28: Karşılaştırma — Hangi Projede Hangisi?
- Bölüm 7 — Konu 29: Navigator 2.0 (Router, RouteInformationParser, RouterDelegate)
- Bölüm 7 — Konu 30: `go_router` Paketi (Pratik ve Modern Çözüm)
- Bölüm 7 — Konu 31: Deep Linking
- Bölüm 7 — Konu 32: Repository Pattern, Katmanlı Mimari (Data/Domain/Presentation)
- Bölüm 7 — Konu 33: Dependency Injection (`get_it`, `injectable`)
- Bölüm 7 — Konu 34: Clean Architecture Uyarlaması, SOLID Prensipleri
- Bölüm 8 — Konu 35: Implicit Animasyonlar
- Bölüm 8 — Konu 36: Explicit Animasyonlar (`AnimationController`, `Tween`, `Curve`)
- Bölüm 8 — Konu 37: Hero Animasyonları
- Bölüm 8 — Konu 38: `CustomPainter` / `Canvas`
- Bölüm 8 — Konu 39: Rive / Lottie Entegrasyonu
- Bölüm 9 — Konu 40: Platform Channels
- Bölüm 9 — Konu 41: Permission Yönetimi (`permission_handler`)
- Bölüm 9 — Konu 42: Kamera, Konum, Sensörler
- Bölüm 9 — Konu 43: Push Notification (Firebase Cloud Messaging)
- Bölüm 9 — Konu 44: Android/iOS Build Sistemleri
- Bölüm 10 — Konu 45: Test Yazımı (Unit, Widget, Integration, Golden)
- Bölüm 10 — Konu 46: CI/CD
- Bölüm 10 — Konu 47: Rebuild Optimizasyonu (`const`, `key` Kullanımı)
- Bölüm 10 — Konu 48: DevTools Profiling
- Bölüm 10 — Konu 49: Lazy Loading, Pagination, Büyük Liste Performansı
- Bölüm 11 — Konu 50: Firebase Ekosistemi (Auth, Firestore, Storage, Functions)
- Bölüm 11 — Konu 51: Supabase Alternatifi
- Bölüm 11 — Konu 52: GraphQL (Opsiyonel)
- Bölüm 11 — Konu 53: Store Yayınlama Süreci (İmzalama, Listing, Versiyonlama)
- Bölüm 11 — Konu 54: App Size, Obfuscation, Flavor Yönetimi
- Bölüm 12 — Konu 55: Flutter Web / Desktop
- Bölüm 12 — Konu 56: Custom `RenderObject` Yazımı
- Bölüm 12 — Konu 57: Engine Mimarisi Derinlemesine (Impeller vs Skia)
- Bölüm 12 — Konu 58: Plugin Geliştirme, pub.dev'e Paket Yayınlama
- Bölüm 12 — Konu 59: Monorepo Mimarisi (Melos)
- Bölüm 12 — Konu 60: Erişilebilirlik (Accessibility) Derinlemesine
- Bölüm 12 — Konu 61: Yerelleştirme (Localization / i18n)
İçindekiler 11 başlık
- Neden Explicit Animasyona İhtiyaç Duyarsın?
- AnimationController — Animasyonun "Motoru"
- AnimationController Aslında Ne Üretiyor?
- Tween — Ham Değeri "Anlamlı" Bir Aralığa Dönüştürme
- Animation<T>'in Tipi double Olmak Zorunda Değil
- AnimatedBuilder'ın child Parametresi — Performans İnceliği
- Durum Dinleme — addStatusListener
- Hazır Geçiş Widget'ları — *Transition Ailesi
- Implicit'e Geri Dönüş — Artık "Sihir" Değil
- 🎯 Bu Dersten Çıkarılması Gerekenler
- 📝 Ödevler
Konu 35'te implicit animasyonların "ne istediğini söyle, Flutter halletsin" felsefesini gördük. Bu derste, "nasıl olacağını sen yönet" dünyasına giriyoruz — implicit animasyonların arkasında gerçekte ne olduğunu da bu sayede öğreneceğiz.
Neden Explicit Animasyona İhtiyaç Duyarsın?
AnimatedContainer gibi widget'lar harika ama şu senaryolarda yetersiz kalır:
- Animasyonu duraklatmak, tersine çevirmek, ya da tekrar tekrar (loop) oynatmak istiyorsun.
- Birden fazla animasyonu birbirine bağlı (biri bitince diğeri başlasın gibi) yönetmen gerekiyor.
- Animasyonun hangi aşamada olduğunu (başladı mı, bitti mi) koda göre tepki vermen gerekiyor.
AnimationController — Animasyonun "Motoru"
class DonenIkon extends StatefulWidget {
const DonenIkon({super.key});
@override
State<DonenIkon> createState() => _DonenIkonState();
}
class _DonenIkonState extends State<DonenIkon> with SingleTickerProviderStateMixin {
late final AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this, // birazdan açıklayacağız
)..repeat(); // sonsuz döngüde oynat
}
@override
void dispose() {
_controller.dispose(); // ÇOK ÖNEMLİ — bellek sızıntısını önler
super.dispose();
}
@override
Widget build(BuildContext context) {
return RotationTransition(
turns: _controller,
child: const Icon(Icons.refresh, size: 48),
);
}
}Parça parça inceleyelim:
with SingleTickerProviderStateMixin — Dart Bölüm 7'de öğrendiğimiz mixin kavramının, Flutter'da kritik bir kullanımı. Bir animasyonun "her karede" (saniyede ~60 kez) güncellenmesi gerekir — bu düzenli "tık"ları üreten mekanizmaya Ticker denir. SingleTickerProviderStateMixin, State sınıfına, bir tane Ticker üretebilme yeteneği ekler (birden fazla AnimationController yönetmen gerekiyorsa TickerProviderStateMixin kullanılır — çoğul).
vsync: this — vsync ("vertical sync"), AnimationController'a hangi Ticker sağlayıcısını kullanacağını söyler. this, yukarıdaki mixin sayesinde artık bir TickerProvider'dır. Neden gerekli? Ekranda görünmeyen bir widget'ın (örneğin başka bir sekmeye geçtin) arka planda gereksiz yere animasyon karesi üretip pil tüketmesini önlemek için — vsync, Flutter'a "bu widget görünür değilken animasyonu duraklat" demenin yoludur.
late final AnimationController _controller — Dart Bölüm 2'de öğrendiğimiz late anahtar kelimesi burada zorunlu, çünkü _controller, initState() içinde (constructor'da değil) oluşturuluyor — late, "bu alana hemen değil, ilk kullanılmadan önce bir değer atanacak" demektir.
..repeat() — Dart Bölüm 6'da öğrendiğimiz cascade operatörü (..) — AnimationController'ı oluşturduktan hemen sonra, aynı ifade içinde repeat() metodunu çağırıyoruz (animasyonu sürekli tekrar ettir).
dispose() içinde _controller.dispose() — Bölüm 2 Konu 6'da State sınıfının yaşam döngüsünü öğrenmiştik; dispose(), widget kalıcı olarak kaldırıldığında çağrılır. AnimationController'ı burada serbest bırakmazsan, widget ekrandan kalksa bile animasyon arka planda çalışmaya devam eder — bu, gerçek uygulamalarda çok yaygın bir bellek sızıntısı kaynağıdır.
AnimationController Aslında Ne Üretiyor?
AnimationController, kendisi bir Animation<double>'dır — 0.0'dan 1.0'a (varsayılan olarak), duration boyunca doğrusal olarak ilerleyen bir değer üretir. RotationTransition'ın turns: parametresi, bu 0.0→1.0 değerini "kaç tam tur" olarak yorumluyor (1.0 = 360°).
_controller.forward(); // 0.0'dan 1.0'a ilerlet
_controller.reverse(); // 1.0'dan 0.0'a geri sar
_controller.repeat(); // sürekli 0.0 → 1.0 → 0.0 → 1.0 ... tekrarla
_controller.stop(); // olduğu yerde durdurTween — Ham Değeri "Anlamlı" Bir Aralığa Dönüştürme
AnimationController'ın ürettiği 0.0–1.0 aralığı çoğu zaman doğrudan işine yaramaz — bir rengi, bir boyutu, bir konumu animasyonlamak istiyorsun. Tween (Konu 35'te implicit animasyonlarda da gördüğümüz sınıf), bu ham 0.0–1.0 değerini, istediğin aralığa dönüştürür:
class BuyuyenKutu extends StatefulWidget {
const BuyuyenKutu({super.key});
@override
State<BuyuyenKutu> createState() => _BuyuyenKutuState();
}
class _BuyuyenKutuState extends State<BuyuyenKutu> with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _boyutAnimasyonu;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
_boyutAnimasyonu = Tween<double>(begin: 50, end: 200).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _boyutAnimasyonu,
builder: (context, child) {
return Container(
width: _boyutAnimasyonu.value,
height: _boyutAnimasyonu.value,
color: Colors.indigo,
);
},
);
}
}Tween<double>(begin: 50, end: 200).animate(...) — Tween'in .animate() metodu, kendisine bir Animation<double> (genelde CurvedAnimation üzerinden _controller'ın kendisi) bağlar ve sonuç olarak, _controller 0.0'dan 1.0'a ilerlerken, 50'den 200'e ilerleyen yeni bir Animation<double> üretir.
CurvedAnimation(parent: _controller, curve: Curves.easeInOut) — _controller'ın doğrusal (linear) ilerlemesine, Konu 35'te gördüğümüz Curves mantığını uygular. Bu, Tween ile AnimationController arasına giren bir "ara katman" olarak düşünebilirsin.
AnimatedBuilder — Bölüm 3 Konu 11'de öğrendiğimiz builder pattern'in burada kritik bir kullanımı. animation: _boyutAnimasyonu verildiğinde, _boyutAnimasyonu her değiştiğinde (saniyede ~60 kez), builder yeniden çağrılır — sen setState() çağırmana hiç gerek kalmadan. _boyutAnimasyonu.value, o anki ara değeri (örn. 127.3) verir.
Neden setState() değil de AnimatedBuilder? Performans. setState() çağırmak, tüm build() metodunu yeniden çalıştırır. AnimatedBuilder, sadece kendi builder'ını (genelde küçük bir widget alt ağacını) yeniden çizer — geri kalan widget ağacın etkilenmez.
Animation<T>'in Tipi double Olmak Zorunda Değil
final Animation<Color?> _renkAnimasyonu = ColorTween(
begin: Colors.blue,
end: Colors.red,
).animate(_controller);
final Animation<Offset> _konumAnimasyonu = Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.5, 0),
).animate(_controller);Dart Bölüm 9'da öğrendiğimiz generic tip parametreleri, burada tam güçle çalışıyor — ColorTween, iki renk arasında (Flutter'ın renk enterpolasyon mantığıyla) geçiş üretir; Offset ile ise widget'ın konumunu animasyonlayabilirsin (SlideTransition ile birlikte kullanılır).
AnimatedBuilder'ın child Parametresi — Performans İnceliği
AnimatedBuilder(
animation: _controller,
child: const FlutterLogo(size: 100), // SADECE BİR KERE oluşturulur
builder: (context, child) {
return Transform.rotate(
angle: _controller.value * 6.28, // 2*pi
child: child, // her karede YENİDEN OLUŞTURULMUYOR, sadece döndürülüyor
);
},
)Eğer builder içinde her karede yeniden oluşturulmasına gerek olmayan bir alt widget varsa (burada FlutterLogo, sadece döndürülüyor, kendisi değişmiyor), bunu AnimatedBuilder'ın child: parametresine taşırsın. Flutter, bu child'ı bir kere oluşturur ve her builder çağrısında aynı örneği sana geri verir — Bölüm 9'da (performans) derinlemesine göreceğimiz bir optimizasyon deseninin ilk örneği.
Durum Dinleme — addStatusListener
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
_controller.reverse(); // bitince otomatik geri sar — "yo-yo" efekti
} else if (status == AnimationStatus.dismissed) {
_controller.forward(); // baştan başlat
}
});AnimationStatus, Dart Bölüm 7'de öğrendiğimiz bir enum'dur (dismissed, forward, reverse, completed). Bu dinleyici sayesinde, animasyon bittiğinde koda göre tepki verebilirsin — implicit animasyonlarda bu seviyede kontrol mümkün değildi.
Hazır Geçiş Widget'ları — *Transition Ailesi
AnimatedBuilder ile her şeyi elle yazabilirsin, ama Flutter, en sık ihtiyaç duyulan geçişler için hazır widget'lar sunar:
FadeTransition(opacity: _animasyon, child: ...) // saydamlık
ScaleTransition(scale: _animasyon, child: ...) // boyut
RotationTransition(turns: _animasyon, child: ...) // döndürme
SlideTransition(position: _konumAnimasyonu, child: ...) // konum
SizeTransition(sizeFactor: _animasyon, child: ...) // boyut açılma/kapanmaBunlar, AnimatedBuilder + ilgili Transform/Opacity widget'ının hazır, isimlendirilmiş kombinasyonlarıdır — kendi AnimatedBuilder'ını yazmadan önce, ihtiyacının bunlardan biriyle karşılanıp karşılanmadığını kontrol etmek iyi bir alışkanlıktır.
Implicit'e Geri Dönüş — Artık "Sihir" Değil
Konu 35'te AnimatedContainer'ın arkasında ne olduğunu merak etmiştin belki — şimdi cevabı biliyorsun: AnimatedContainer, kendi iç AnimationController'ını yönetir, değer değiştiğinde otomatik olarak .forward() çağırır ve bir Tween oluşturup animasyonu senin için oynatır. Konu 35'teki her implicit widget, bu dersteki parçaların Flutter tarafından senin için birleştirilmiş halidir — tıpkı Provider'ın arka planda InheritedWidget kullanması gibi (Bölüm 6 Konu 23'ü hatırla).
🎯 Bu Dersten Çıkarılması Gerekenler
AnimationController,0.0–1.0arası doğrusal ilerleyen birAnimation<double>üretir;vsync(Ticker sağlayıcı) ile senkronize çalışır ve mutlakadispose()edilmelidir.Tween<T>.animate(...), ham0.0–1.0değerini istediğin aralığa/tipe (double,Color,Offsetvb.) dönüştürür;CurvedAnimation, buna birCurveuygular.AnimatedBuilder, birAnimationdeğiştiğindesetState()çağırmadan, sadece kendi builder'ını yeniden çizer — performans içinchild:parametresiyle değişmeyen alt widget'ları izole edebilirsin.addStatusListener, animasyonun aşamasına (completed,dismissedvb.) göre koda tepki vermeni sağlar.FadeTransition/ScaleTransition/RotationTransition/SlideTransition, en sık ihtiyaç duyulan geçişlerin hazır widget'larıdır.- Implicit animasyonlar (Konu 35), bu dersteki mekanizmaların Flutter tarafından senin için sarmalanmış halidir.
📝 Ödevler
- [ ]
AnimationController+RotationTransitionile sürekli dönen bir ikon yap,.stop()/.forward()bağlı iki butonla kontrol et. - [ ]
Tween<double>+CurvedAnimation+AnimatedBuilderile, bir kutunun boyutunuCurves.bounceOuteğrisiyle büyüten bir animasyon yaz. - [ ]
addStatusListenerkullanarak, bir animasyon bittiğinde otomatik olarak tersine (reverse) başlayan bir "yo-yo" efekti oluştur. - [ ]
AnimatedBuilder'ınchild:parametresini kullanarak, sabit kalan bir widget'ı (örn. bir resim) her karede yeniden oluşturmadan döndür. - [ ] Aynı görsel efekti önce elle
AnimatedBuilderile, sonra hazırFadeTransition/ScaleTransitionwidget'larıyla yap, kod miktarını karşılaştır.
Sıradaki konu: Bölüm 8 — Konu 37: Hero Animasyonları