↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 8 — Konu 35: Implicit Animasyonlar

4 dk okuma #flutter
Dizi · 35/61 Flutter Türkçe Tutorial
  1. Bölüm 1 — Konu 1: Flutter Nedir, Mimarisi (Widget → Element → RenderObject, Skia/Impeller)
  2. Bölüm 1 — Konu 2: Ortam Kurulumu (Flutter SDK, Android Studio/VS Code, Emulator/Simulator, DevTools'a İlk Bakış)
  3. Bölüm 1 — Konu 3: İlk Proje Yapısı (`pubspec.yaml`, `lib/`, Klasör Mimarisi, `flutter create` Anatomisi)
  4. Bölüm 1 — Konu 4: Hot Reload / Hot Restart Ne Yapıyor, Neden Önemli
  5. Bölüm 2 — Konu 5: "Her Şey Widget'tır" Felsefesi, Widget Ağacı
  6. Bölüm 2 — Konu 6: StatelessWidget vs StatefulWidget + `setState()` Derinlemesine (Rebuild Mekanizması)
  7. Bölüm 2 — Konu 7: Temel Layout — Container, Row, Column, Stack, Padding, Align, Center
  8. Bölüm 2 — Konu 8: Constraint Sistemi — "Constraints Go Down, Sizes Go Up, Parent Sets Position"
  9. Bölüm 2 — Konu 9: `Expanded`, `Flexible`, `Spacer`, Intrinsic Widget'lar
  10. Bölüm 3 — Konu 10: MaterialApp/CupertinoApp, Scaffold, AppBar
  11. Bölüm 3 — Konu 11: `ListView`, `GridView`, `SingleChildScrollView` (+ Builder Pattern)
  12. Bölüm 3 — Konu 12: Text, TextStyle, Icon, Image, Asset Yönetimi
  13. Bölüm 3 — Konu 13: Custom Widget Yazma Prensipleri (Composition Over Inheritance)
  14. Bölüm 3 — Konu 14: Navigator 1.0 — Push/Pop, Named Routes
  15. Bölüm 4 — Konu 15: Form, TextField/TextFormField, GlobalKey<FormState>, Validasyon
  16. Bölüm 4 — Konu 16: Theme Sistemi (ThemeData, ColorScheme)
  17. Bölüm 4 — Konu 17: Responsive & Adaptive Tasarım (MediaQuery, LayoutBuilder, OrientationBuilder, Breakpoint Stratejileri)
  18. Bölüm 5 — Konu 18: Future/Async-Await Flutter Bağlamında, FutureBuilder
  19. Bölüm 5 — Konu 19: `Stream`, `StreamBuilder`
  20. Bölüm 5 — Konu 20: HTTP İstekleri (`http` / `dio` Paketleri)
  21. Bölüm 5 — Konu 21: JSON Serialization (Manuel → `json_serializable`/`freezed`)
  22. Bölüm 5 — Konu 22: Local Storage — `shared_preferences` → `Hive` → `sqflite`/Drift
  23. Bölüm 6 — Konu 23: InheritedWidget ve InheritedModel — "Neden setState Yetmiyor" Sorusunun Cevabı
  24. Bölüm 6 — Konu 24: Provider Paketi
  25. Bölüm 6 — Konu 25: Riverpod (Modern Yaklaşım, Provider'ın Halefi)
  26. Bölüm 6 — Konu 26: BLoC/Cubit Pattern (`flutter_bloc`)
  27. Bölüm 6 — Konu 27: GetX (Tartışmalı Ama Yaygın)
  28. Bölüm 6 — Konu 28: Karşılaştırma — Hangi Projede Hangisi?
  29. Bölüm 7 — Konu 29: Navigator 2.0 (Router, RouteInformationParser, RouterDelegate)
  30. Bölüm 7 — Konu 30: `go_router` Paketi (Pratik ve Modern Çözüm)
  31. Bölüm 7 — Konu 31: Deep Linking
  32. Bölüm 7 — Konu 32: Repository Pattern, Katmanlı Mimari (Data/Domain/Presentation)
  33. Bölüm 7 — Konu 33: Dependency Injection (`get_it`, `injectable`)
  34. Bölüm 7 — Konu 34: Clean Architecture Uyarlaması, SOLID Prensipleri
  35. Bölüm 8 — Konu 35: Implicit Animasyonlar
  36. Bölüm 8 — Konu 36: Explicit Animasyonlar (`AnimationController`, `Tween`, `Curve`)
  37. Bölüm 8 — Konu 37: Hero Animasyonları
  38. Bölüm 8 — Konu 38: `CustomPainter` / `Canvas`
  39. Bölüm 8 — Konu 39: Rive / Lottie Entegrasyonu
  40. Bölüm 9 — Konu 40: Platform Channels
  41. Bölüm 9 — Konu 41: Permission Yönetimi (`permission_handler`)
  42. Bölüm 9 — Konu 42: Kamera, Konum, Sensörler
  43. Bölüm 9 — Konu 43: Push Notification (Firebase Cloud Messaging)
  44. Bölüm 9 — Konu 44: Android/iOS Build Sistemleri
  45. Bölüm 10 — Konu 45: Test Yazımı (Unit, Widget, Integration, Golden)
  46. Bölüm 10 — Konu 46: CI/CD
  47. Bölüm 10 — Konu 47: Rebuild Optimizasyonu (`const`, `key` Kullanımı)
  48. Bölüm 10 — Konu 48: DevTools Profiling
  49. Bölüm 10 — Konu 49: Lazy Loading, Pagination, Büyük Liste Performansı
  50. Bölüm 11 — Konu 50: Firebase Ekosistemi (Auth, Firestore, Storage, Functions)
  51. Bölüm 11 — Konu 51: Supabase Alternatifi
  52. Bölüm 11 — Konu 52: GraphQL (Opsiyonel)
  53. Bölüm 11 — Konu 53: Store Yayınlama Süreci (İmzalama, Listing, Versiyonlama)
  54. Bölüm 11 — Konu 54: App Size, Obfuscation, Flavor Yönetimi
  55. Bölüm 12 — Konu 55: Flutter Web / Desktop
  56. Bölüm 12 — Konu 56: Custom `RenderObject` Yazımı
  57. Bölüm 12 — Konu 57: Engine Mimarisi Derinlemesine (Impeller vs Skia)
  58. Bölüm 12 — Konu 58: Plugin Geliştirme, pub.dev'e Paket Yayınlama
  59. Bölüm 12 — Konu 59: Monorepo Mimarisi (Melos)
  60. Bölüm 12 — Konu 60: Erişilebilirlik (Accessibility) Derinlemesine
  61. Bölüm 12 — Konu 61: Yerelleştirme (Localization / i18n)
Dizinin sayfası →
İçindekiler 9 başlık
  1. Implicit vs Explicit — Temel Ayrım
  2. AnimatedContainer — En Sık Kullanılan Implicit Widget
  3. Diğer Yaygın Animated* Widget'lar
  4. AnimatedSwitcher ve key — Neden Gerekli?
  5. TweenAnimationBuilder — Implicit Dünyanın "Joker Kartı"
  6. Curves — Animasyonun "Kişiliği"
  7. Ne Zaman Implicit, Ne Zaman Explicit?
  8. 🎯 Bu Dersten Çıkarılması Gerekenler
  9. 📝 Ödevler

Bölüm 7'de mimariyi sağlamlaştırdık — Bölüm 8'de işin görsel tarafına, animasyonlara giriyoruz. Bu bölümü iki gruba ayıracağız: implicit (örtük, bu derste) ve explicit (açık, Konu 36'da) animasyonlar. Aradaki farkı baştan netleştirelim çünkü bu, bölümün tamamının zihinsel çerçevesi.

Implicit vs Explicit — Temel Ayrım

Implicit animasyon: Sadece hedef değeri değiştirirsin (width: 100 → width: 200), widget kendi kendine aradaki geçişi animasyonlu şekilde yapar. Sen bir AnimationController, bir Tween yönetmezsin — Flutter senin için hallediyor.

Explicit animasyon: Animasyonun her aşamasını (süre, eğri, ne zaman başlayıp durduğu) sen kontrol edersin — bir AnimationController ile (Konu 36'da göreceğiz).

Analoji: Implicit animasyon, Dart Bölüm 5'te öğrendiğimiz yüksek seviye koleksiyon metodlarına (.map(), .where()) benzer — "ne istediğini" söylersin, "nasıl yapılacağını" Flutter halleder. Explicit animasyon ise, aynı işi elle bir for döngüsüyle yazmak gibidir — daha fazla kontrol, daha fazla kod.

AnimatedContainer — En Sık Kullanılan Implicit Widget

dart
class AnimasyonluKutu extends StatefulWidget {
  const AnimasyonluKutu({super.key});

  @override
  State<AnimasyonluKutu> createState() => _AnimasyonluKutuState();
}

class _AnimasyonluKutuState extends State<AnimasyonluKutu> {
  bool _buyukMu = false;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        setState(() {
          _buyukMu = !_buyukMu; // Bölüm 2 Konu 6 — setState ile rebuild tetikleme
        });
      },
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeInOut,
        width: _buyukMu ? 200 : 100,
        height: _buyukMu ? 200 : 100,
        decoration: BoxDecoration(
          color: _buyukMu ? Colors.indigo : Colors.blue,
          borderRadius: BorderRadius.circular(_buyukMu ? 40 : 8),
        ),
      ),
    );
  }
}

Nasıl çalıştığını parça parça açalım:

  • duration: const Duration(milliseconds: 300) — bu, geçişin ne kadar süreceğini belirtir (Dart Bölüm 1'de öğrendiğimiz Duration sınıfı).
  • curve: Curves.easeInOut — animasyonun hızlanma/yavaşlama karakteri. Curves, Flutter'ın hazır sunduğu bir dizi matematiksel eğridir (linear sabit hız, easeIn yavaş başlar hızlanır, bounceOut sekerek biter, vb.). Bunu birazdan detaylandıracağız.
  • width/height/decoration — normal bir Container'daki (Bölüm 2 Konu 7) parametrelerin aynısı! Fark şu: AnimatedContainer, bu değerlerden biri değiştiğinde (burada _buyukMu setState ile değiştiğinde), yeni değere anında zıplamak yerine, duration boyunca kademeli olarak geçiş yapar.

Kritik zihinsel model: Sen setState() içinde hedef değeri değiştiriyorsun (_buyukMu = !_buyukMu), AnimatedContainer ise eski değerden yeni değere giden ara kareleri kendisi hesaplayıp çiziyor. Bu, Bölüm 2'de öğrendiğimiz "anında yeniden çizim" modelinden farklı — burada widget, birden fazla frame boyunca kendi kendini günceller.

Diğer Yaygın Animated* Widget'lar

dart
// Görünürlük/saydamlık
AnimatedOpacity(
  duration: const Duration(milliseconds: 200),
  opacity: _gorunurMu ? 1.0 : 0.0,
  child: const Icon(Icons.star),
)

// Konumlandırma (bir Stack içinde)
AnimatedPositioned(
  duration: const Duration(milliseconds: 400),
  left: _solaMi ? 0 : 200,
  top: 50,
  child: const FlutterLogo(),
)

// Metin stili
AnimatedDefaultTextStyle(
  duration: const Duration(milliseconds: 300),
  style: TextStyle(
    fontSize: _buyukMu ? 32 : 16,
    color: _buyukMu ? Colors.red : Colors.black,
  ),
  child: const Text('Merhaba'),
)

// İçerik değişimi (fade + boyut geçişi ile)
AnimatedSwitcher(
  duration: const Duration(milliseconds: 250),
  child: Text(
    '$_sayac',
    key: ValueKey<int>(_sayac), // ÖNEMLİ — birazdan açıklayacağız
    style: const TextStyle(fontSize: 48),
  ),
)

Deseni fark ettin mi? *Her `Animatedwidget, normal bir widget'ın (Container,Opacity,Positioned,DefaultTextStyle`) "değer değiştiğinde otomatik geçiş yapan" versiyonudur. Bu, Bölüm 3 Konu 13'te öğrendiğimiz kompozisyon prensibinin** Flutter'ın kendi API tasarımında tutarlı şekilde uygulandığını gösteriyor — aynı kalıp, düzinelerce widget için tekrarlanıyor.

AnimatedSwitcher ve key — Neden Gerekli?

AnimatedSwitcher'daki key: ValueKey<int>(_sayac) satırı, ilk bakışta gizemli görünebilir. Şunu anlaman gerekiyor: Flutter, bir widget'ın "aynı widget mı yoksa yeni bir widget mı" olduğuna, büyük ölçüde tipine ve key'ine bakarak karar verir (Bölüm 1'de değindiğimiz widget ağacı diffing mekanizmasının bir parçası). AnimatedSwitcher, çocuğu değiştiğinde (eski widget'tan farklı bir widget geldiğinde) geçiş animasyonu oynatır — ama sadece Text('$_sayac') yazarsan, Flutter bunu "aynı Text widget'ı, sadece içeriği değişti" olarak görür, yeni bir widget olarak algılamaz, ve animasyon tetiklenmez. ValueKey<int>(_sayac) vererek, Flutter'a "sayaç değeri değiştiğinde, bu artık farklı bir widget'tır" demiş oluyoruz — bu da AnimatedSwitcher'ın geçiş animasyonunu tetiklemesini sağlıyor.

TweenAnimationBuilder — Implicit Dünyanın "Joker Kartı"

Bazen ihtiyacın olan animasyon, hazır bir Animated* widget'ıyla karşılanmaz (örneğin bir sayının 0'dan 100'e animasyonlu şekilde sayması). TweenAnimationBuilder, implicit animasyon mantığını herhangi bir değere uygulamanı sağlar:

dart
TweenAnimationBuilder<double>(
  tween: Tween<double>(begin: 0, end: 100),
  duration: const Duration(seconds: 2),
  builder: (context, deger, child) {
    return Text('${deger.toInt()}'); // 0'dan 100'e animasyonlu sayma
  },
)

Tween<double>(begin: 0, end: 100) — Dart Bölüm 9'da öğrendiğimiz generic (<double>), burada hangi tip arasında geçiş yapılacağını belirtiyor. Tween ("between" kelimesinden), iki değer arasında ara değerleri hesaplayan bir sınıftır — Konu 36'da explicit animasyonlarda da aynı Tween sınıfını göreceğiz, çünkü implicit ve explicit animasyonlar aynı temel altyapıyı (Konu 36'da tam işleyeceğimiz Animation/AnimationController) paylaşır; TweenAnimationBuilder, bu altyapıyı senin için gizleyen bir implicit sarmalayıcıdır.

builder: (context, deger, child) — Bölüm 3 Konu 11'de öğrendiğimiz builder pattern'in bir başka örneği; her animasyon karesinde, o anki ara değerle (deger) yeniden çağrılır.

Curves — Animasyonun "Kişiliği"

dart
Curves.linear      // sabit hız — robotik, doğal hissettirmez
Curves.easeIn      // yavaş başlar, hızlanarak biter
Curves.easeOut     // hızlı başlar, yavaşlayarak biter
Curves.easeInOut   // yavaş başlar, ortada hızlanır, yavaş biter (EN yaygın kullanılan)
Curves.bounceOut   // sona doğru seker (sekme efekti)
Curves.elasticOut  // esnek/yaylı bir aşırı-gitme efekti

Gerçek dünyada hiçbir hareket sabit hızda (linear) olmaz — bir araba durmadan önce yavaşlar, bir top zıplarken sekerek durur. curve, animasyonuna bu doğal hissi kazandırır. Pratik tavsiye: emin değilsen Curves.easeInOut ile başla, çoğu UI animasyonu için doğal ve nötr bir seçimdir.

Ne Zaman Implicit, Ne Zaman Explicit?

Durum Tercih
Tek bir değer değişince basit bir geçiş (boyut, renk, opaklık) Implicit (AnimatedContainer vb.)
Animasyonu duraklat/devam ettir/tersine çevir gibi ince kontrol gerekiyor Explicit (Konu 36)
Birden fazla animasyonu eşzamanlı, birbirine bağlı şekilde yönetmek gerekiyor Explicit
Kullanıcı etkileşimiyle sürekli tetiklenen basit geçişler (buton basılı tutma, hover) Implicit genelde yeterli

Pratik gerçek: implicit animasyonlar, günlük UI işlerinin çoğunu (%80'e yakınını) karşılar — basit, az kod, okunabilir. Explicit animasyonlara, gerçekten ince kontrol gerektiğinde geçersin.


🎯 Bu Dersten Çıkarılması Gerekenler

  • Implicit animasyonlarda sadece hedef değeri değiştirirsin, widget geçişi kendisi yönetir; explicit animasyonlarda (Konu 36) süreci sen kontrol edersin.
  • AnimatedContainer/AnimatedOpacity/AnimatedPositioned/AnimatedDefaultTextStyle, normal widget'ların "otomatik geçişli" versiyonlarıdır — hepsi aynı kalıbı (duration + curve + hedef değerler) paylaşır.
  • AnimatedSwitcher, çocuk widget değiştiğinde geçiş animasyonu oynatır — Flutter'ın "bu farklı bir widget" diye anlayabilmesi için genelde bir key (örn. ValueKey) vermen gerekir.
  • TweenAnimationBuilder, implicit animasyon mantığını herhangi bir değere (sadece widget parametrelerine değil) uygulamanı sağlar; Tween, explicit animasyonlarla paylaşılan temel bir kavramdır.
  • Curves, animasyona doğal bir hız karakteri kazandırır; easeInOut güvenli bir varsayılandır.

📝 Ödevler

  • [ ] AnimatedContainer ile, tıklandığında boyutu, rengi VE köşe yuvarlaklığını aynı anda değiştiren bir kutu yap.
  • [ ] AnimatedOpacity ile, bir butona basıldığında yavaşça kaybolup tekrar beliren bir widget yap.
  • [ ] AnimatedSwitcher kullanarak bir sayaç değerini, key vermeden ve vererek iki şekilde dene — aradaki görsel farkı gözlemle ve nedenini açıkla.
  • [ ] TweenAnimationBuilder ile 0'dan istediğin bir sayıya, 2 saniyede animasyonlu şekilde sayan bir "istatistik" widget'ı yap.
  • [ ] Aynı AnimatedContainer örneğini, sırasıyla Curves.linear, Curves.bounceOut ve Curves.elasticOut ile dene, aralarındaki "his" farkını kendi cümlelerinle tarif et.

Sıradaki konu: Bölüm 8 — Konu 36: Explicit Animasyonlar (AnimationController, Tween, Curve — Tam Kontrol)