- Önce hangi animasyon türüne ihtiyacınız olduğuna karar verin
- Örtük animasyon nasıl kurulur
- Açık animasyon: AnimationController adım adım
- Takılmayı tahminle değil ölçümle bulun
- build maliyetini düşürmenin pratik yolları
- Listeler, opaklık, kırpma ve saveLayer
- Sık yapılan hatalar ve kontrol listesi
- Mindset programlarında nerede çalışılır
- Sık sorulan sorular
Flutter'da akıcı animasyon kurmak ve takılmaları gidermek
Flutter'da örtük ve açık animasyon nasıl kurulur, takılmalar nasıl ölçülür ve build maliyeti, liste, opaklık ve kırpma tarafında hangi ayarlar yapılır.
Önce hangi animasyon türüne ihtiyacınız olduğuna karar verin
Flutter dokümantasyonu animasyonları ne kadar kontrol istediğinize göre ayırır. Örtük (implicit) animasyonda bir widget'ın özelliği değişir ve çatı bu değişimi sizin yerinize yumuşak bir geçişe çevirir. Açık (explicit) animasyonda ise bir AnimationController ile başlatma, durdurma, ters çevirme ve tekrar etme sizin elinizdedir.
Karar ölçütü basittir. Bir kutunun rengi, boyutu ya da konumu bir duruma bağlı olarak değişiyorsa ve animasyonun ortasında müdahale etmeniz gerekmiyorsa örtük animasyon yeterlidir. Animasyon sürekli dönüyorsa, kullanıcı hareketine bağlıysa, birden çok animasyon aynı anda koordine edilecekse ya da bitiş durumuna göre başka bir iş tetiklenecekse açık animasyona geçin.
İki yolun arasında özel bir köprü de vardır: TweenAnimationBuilder, kendi örtük animasyonunuzu controller yazmadan kurmanızı sağlar. Aşırı mühendislikten kaçınmak için sırayla deneyin: önce hazır Animated... widget'ları, sonra TweenAnimationBuilder, en son AnimationController.
Örtük animasyon nasıl kurulur
En kısa yol AnimatedContainer, AnimatedOpacity, AnimatedAlign gibi hazır widget'lardır. Hepsi aynı kalıbı izler: süre verirsiniz, bir durum değişkeni değiştiğinde yeni değer atanır ve çatı eski değerden yenisine geçişi çizer.
Örnek kalıp:
AnimatedContainer( duration: gecisSuresi, curve: Curves.easeInOut, width: genisletildi ? genis : dar, decoration: BoxDecoration( color: genisletildi ? Colors.blue : Colors.grey, borderRadius: BorderRadius.circular(yaricap), ), )
Butona basınca setState içinde genisletildi değerini tersine çevirmeniz yeterlidir. Hazır widget'ın karşılamadığı bir özelliği animasyonlamak gerekirse TweenAnimationBuilder kullanın: başlangıç ve bitiş değerini bir Tween ile verir, builder içinde o anki değeri widget'a bağlarsınız.
Bu aşamada şu kontroller işinizi kurtarır. Eğri (curve) seçimi hissi belirler: doğrusal hareket mekanik görünür, easeInOut gibi eğriler doğal durur. Süreyi tek bir sabitte tutun ki ekranlar arası tutarlılık sağlansın.
Açık animasyon: AnimationController adım adım
Açık animasyonda State sınıfınıza SingleTickerProviderStateMixin eklenir (birden çok controller varsa TickerProviderStateMixin). Controller initState içinde oluşturulur ve vsync olarak State nesnesinin kendisi verilir. Dokümantasyona göre vsync, ekran dışında kalan animasyonların gereksiz kaynak tüketmesini engeller.
İskelet şöyledir:
@override void initState() { super.initState(); controller = AnimationController(duration: animasyonSuresi, vsync: this); animation = CurvedAnimation(parent: controller, curve: Curves.easeIn); }
@override void dispose() { controller.dispose(); super.dispose(); }
Controller varsayılan olarak başlangıçtan bitişe doğru artan bir değer üretir. Bu değeri gerçek aralığa çevirmek için Tween kullanılır: Tween<double>(begin: baslangic, end: bitis).animate(controller). Renk için ColorTween, tam sayı için IntTween vardır. Başlatmak için controller.forward(), tersine çevirmek için reverse(), döngü için repeat() çağrılır. Bitişe tepki vermek isterseniz addStatusListener ile dismissed, forward, reverse, completed durumlarını dinlersiniz.
Çizimi controller'a bağlamanın temiz yolu AnimatedBuilder'dır; addListener içinde setState çağırmak çalışır ama tüm ekranı yeniden kurma riski taşır. Ekranlar arası geçişte aynı öğeyi uçuran Hero widget'ı ise Navigator ile birlikte çalışır ve ilgili sayfalarda aynı etiketle işaretlenen öğeyi animasyonlu taşır.
Takılmayı tahminle değil ölçümle bulun
Performans çalışmasının ilk kuralı ölçmektir. Flutter dokümantasyonu performansı debug modunda değil profile modunda ölçmenizi söyler; debug modu kasıtlı olarak yavaştır ve yanıltıcı sonuç verir. Gerçek bir cihazda profile modunda çalıştırıp DevTools'un Performance görünümünü açın. Uzun süren kareler orada işaretlenir ve hangi kısmın yavaş olduğu, kullanıcı arayüzü iş parçacığında mı yoksa çizim tarafında mı olduğu görülür.
Çatının sağladığı kare bütçesi çok kısadır: ekran yenileme hızı yükseldikçe bir kareye ayrılan süre daha da azalır. Bu yüzden bir kare içinde hem build hem çizim işinin birlikte sığması gerekir. Takılma gördüğünüzde sorulacak soru şudur: build mi pahalı, yoksa çizim mi?
IDE eklentisindeki Flutter Performance penceresinde widget yeniden kurulum bilgisini göstermek de işe yarar. Gereksiz yere her karede yeniden kurulan bir alt ağaç, animasyon takılmasının en yaygın kaynağıdır.
build maliyetini düşürmenin pratik yolları
Resmî önerilerin merkezinde build() içinde tekrarlayan ve pahalı iş yapmamak yer alır, çünkü build bir üst widget yeniden kurulduğunda çalışır. Şunları uygulayın:
- const kurucuları kullanın; değişmeyen widget'lar yeniden kurulurken atlanır. flutter_lints paketi bunu hatırlatır.
- setState'i yalnızca gerçekten değişen en küçük alt ağaçta çağırın. Üst düzey bir State içinde setState tüm torunları yeniden kurar.
- Yeniden kullanılan arayüz parçası için yardımcı fonksiyon yerine StatelessWidget yazın.
- AnimatedBuilder'da animasyondan bağımsız ağır alt ağacı child parametresi olarak geçirin. Builder her animasyon adımında çalışır; child ise bir kez kurulur.
- Döngüde metin birleştirirken artı işareti yerine StringBuffer kullanın.
Bir uyarı daha: Widget nesneleri üzerinde operator == tanımlamayın. Dokümantasyona göre bu, uygulamanın geneline yayılan bir performans düşüşüne yol açabilir.
Listeler, opaklık, kırpma ve saveLayer
Uzun listelerde Column ya da çocuk listesi hazır verilen ListView yerine ListView.builder kullanın; böylece yalnızca ekranda görünen öğeler kurulur. Hücre boyutlarını tüm hücrelere bakarak hesaplatan IntrinsicHeight ve IntrinsicWidth ek bir yerleşim geçişi doğurur; mümkünse boyutları önceden sabitleyin.
Opacity widget'ı animasyon içinde pahalıdır. Dokümantasyonun önerisi, animasyonda Opacity yerine AnimatedOpacity ya da FadeInImage kullanmaktır; basit şekil ve metinde ise yarı saydam renk vermek yeterlidir. Köşe yuvarlatmak için ClipRRect sarmak yerine widget'ın borderRadius özelliğini kullanın, animasyon sırasında kırpmadan kaçının.
saveLayer() ekran dışı bir tampon ayırır ve mobil GPU'larda pahalıdır. ShaderMask, ColorFilter ve bazı Chip ile Text durumları bunu çağırabilir. Şüphelendiğinizde PerformanceOverlayLayer.checkerboardOffscreenLayers seçeneğini açıp DevTools'ta inceleyin, üst üste binen yarı saydam şekilleri önceden hesaplayıp önbelleğe almayı deneyin.
Sık yapılan hatalar ve kontrol listesi
Animasyon ve performans işlerinde tekrar eden hatalar şunlardır: controller'ı dispose etmeyi unutmak ve ticker'ın arka planda çalışmaya devam etmesi, debug modunda ölçüp yanlış sonuca varmak, tüm ekranı kapsayan bir State'te setState çağırmak, animasyon içinde Opacity ya da kırpma kullanmak, liste öğelerini tek seferde kurmak.
İşi bitirmeden önce şu kontrolleri yapın:
- Animasyon gerçek cihazda profile modunda akıcı mı?
- Her AnimationController dispose edildi mi?
- Animasyondan bağımsız alt ağaçlar const ya da child olarak mı verildi?
- Listeler builder ile mi kuruluyor?
- Animasyonlu alanda saveLayer'a yol açan widget var mı?
Bu liste kısa görünür ama takılma şikayetlerinin büyük bölümünü tarif eder; bir ekranı bu sırayla geçtiğinizde sorunun kaynağı genellikle ilk maddelerde çıkar.
Mindset programlarında nerede çalışılır
Flutter ile Mobil Geliştirme programının müfredatında Animasyon ve Performans ayrı bir modüldür; öncesinde Widget Sistemi ve Yerleşim ile State Yönetimi gibi bu sayfadaki tekniklerin dayandığı konular işlenir. Program, Flutter ile mobil ürün geliştiren ya da Dart'a geçen yazılımcı ekipler için uygundur.
Kurumsal ekiplerde bu modül, mevcut bir Flutter uygulamasında takılma şikayeti alan mobil ekibin ortak bir ölçüm ve iyileştirme alışkanlığı kazanması için kullanılabilir.
İlgili Mindset programı
Flutter ile Mobil Geliştirme
Sık sorulan sorular
Flutter'da örtük ve açık animasyon arasındaki fark nedir?
Örtük animasyonda bir widget özelliği değiştiğinde çatı geçişi kendisi çizer, örneğin AnimatedContainer. Açık animasyonda AnimationController ile başlatma, durdurma, tekrar ve yön üzerinde tam kontrol sizdedir. Basit durum geçişleri için örtük, koordinasyon ve sürekli hareket gerektiren işler için açık animasyonu seçin.
Flutter performansını nerede ölçmeliyim?
Gerçek bir cihazda profile modunda ve DevTools'un Performance görünümüyle ölçün. Debug modu kasıtlı olarak yavaştır ve takılma ölçümü için güvenilir değildir. Uzun kareleri bulduktan sonra sorunun build mi yoksa çizim tarafında mı olduğunu ayırın, sonra düzeltmeyi yapıp yeniden ölçün.
const kullanmak animasyon performansına gerçekten yardım eder mi?
Evet, çünkü const olarak kurulan widget'lar üst widget yeniden kurulduğunda atlanır. Animasyon her adımda build tetiklediği için, animasyondan bağımsız alt ağaçların const olması ya da AnimatedBuilder'ın child parametresine verilmesi gereksiz işi doğrudan azaltır.
Opacity widget'ı neden animasyonda önerilmiyor?
Opacity, içeriği ayrı bir katmanda çizmeyi gerektirebilir ve bu maliyetlidir. Resmî öneri, animasyonlarda AnimatedOpacity ya da FadeInImage kullanmak, basit şekil ve metinde ise yarı saydam renk vermektir. Yarı saydamlık üst üste binen parçalar için tek başına yeterli değildir.
AnimationController'ı dispose etmezsem ne olur?
Controller'ın ticker'ı çalışmaya devam eder ve kaynak sızıntısına yol açar; ayrıca State yok olduktan sonra güncelleme denemesi hata üretebilir. dispose() içinde, super.dispose() çağrısından önce controller.dispose() çağırmak standart uygulamadır. Ekranlar arasında sık gezinen uygulamalarda her ziyarette bir controller sızar ve birikir. Kural basittir: controller'ı initState içinde oluşturduysanız dispose içinde serbest bırakın.