Flutter ile Uygulamaları Canlandırma
Yayınlanan: 2022-03-10Herhangi bir platform için uygulamalar, sezgisel olduklarında, iyi göründüklerinde ve kullanıcı etkileşimlerine hoş geri bildirim sağladığında övülür. Animasyon bunu yapmanın yollarından biridir.
Platformlar arası bir çerçeve olan Flutter, son iki yılda web ve masaüstü desteğini içerecek şekilde olgunlaştı. Onunla geliştirilen uygulamaların sorunsuz ve güzel göründüğü konusunda bir ün kazandı. Zengin animasyon desteği, kullanıcı arayüzü yazmanın bildirimsel yolu, “Hot Reload” ve diğer özellikleri ile artık eksiksiz bir platformlar arası çerçevedir.
Flutter ile başlıyorsanız ve animasyon eklemenin alışılmadık bir yolunu öğrenmek istiyorsanız, doğru yerdesiniz: Animasyon eklemenin dolaylı bir yolu olan animasyon ve hareket widget'ları alanını keşfedeceğiz.
Flutter, widget kavramına dayanmaktadır. Bir uygulamanın her görsel bileşeni bir pencere öğesidir; bunları Android'deki görünümler olarak düşünün. Flutter, bir Animation sınıfı, yönetim için bir "AnimationController" nesnesi ve veri aralığını enterpolasyon yapmak için "Tween" kullanarak animasyon desteği sağlar. Bu üç bileşen, düzgün animasyon sağlamak için birlikte çalışır. Bu, animasyonun manuel olarak oluşturulmasını ve yönetilmesini gerektirdiğinden, açık bir animasyon yöntemi olarak bilinir.
Şimdi sizi animasyon ve hareket widget'larıyla tanıştırayım. Flutter, doğası gereği animasyonu destekleyen çok sayıda widget sağlar. Tüm animasyon bu widget kategorisi tarafından işlendiğinden, bir animasyon nesnesi veya herhangi bir denetleyici oluşturmaya gerek yoktur. Sadece gerekli animasyon için uygun widget'ı seçin ve canlandırmak için widget'ın özellik değerlerini iletin. Bu teknik, canlandırmanın örtük bir yoludur.
Yukarıdaki grafik, Flutter'daki animasyon hiyerarşisini, hem açık hem de kapalı animasyonun nasıl desteklendiğini kabaca ortaya koymaktadır.
Bu makalede ele alınan animasyonlu widget'lardan bazıları şunlardır:
-
AnimatedOpacity
-
AnimatedCrossFade
-
AnimatedAlign
-
AnimatedPadding
-
AnimatedSize
-
AnimatedPositioned
.
Flutter, yalnızca önceden tanımlanmış animasyonlu widget'lar sağlamakla kalmaz, aynı zamanda, özel dolaylı olarak animasyonlu widget'lar oluşturmak için kullanılabilen AnimatedWidget
adlı genel bir widget'ı da sağlar. Adından da anlaşılacağı gibi, bu widget'lar, animasyonlu ve hareketli widget'lar kategorisine aittir ve bu nedenle, animasyonları daha pürüzsüz ve daha iyi görünümlü hale getirmemizi sağlayan bazı ortak özelliklere sahiptirler.
Bu ortak özellikleri daha sonra tüm örneklerde kullanılacağı için şimdi açıklayayım.
-
duration
Parametrelerin canlandırılacağı süre. -
reverseDuration
Ters animasyonun süresi. -
curve
Parametreleri canlandırırken uygulanacak eğri. Enterpolasyonlu değerler doğrusal bir dağılımdan alınabilir veya belirtilmişse ve belirtildiğinde bir eğriden alınabilir.
"Alıntılanan" adını vereceğimiz basit bir uygulama oluşturarak yolculuğa başlayalım. Uygulama her başlatıldığında rastgele bir teklif gösterecektir. Unutulmaması gereken iki şey: ilk olarak, tüm bu alıntılar uygulamada sabit kodlanacaktır; ve ikincisi, hiçbir kullanıcı verisi kaydedilmeyecektir.
Not : Bu örnekler için tüm dosyalar GitHub'da bulunabilir.
Başlarken
Flutter yüklenmelidir ve devam etmeden önce temel akışa biraz aşina olmanız gerekir. Başlamak için iyi bir yer, "Gerçekten Platformlar Arası Mobil Geliştirme İçin Google'ın Flutter'ını Kullanmak"tır.
Android Studio'da yeni bir Flutter projesi oluşturun.
Bu, proje temellerini yapılandırabileceğiniz yeni bir proje sihirbazı açacaktır.
Proje türü seçim ekranında, her biri belirli bir senaryoya hitap eden çeşitli Flutter projeleri vardır. Bu eğitim için Flutter Uygulaması'nı seçin ve İleri'ye basın.
Şimdi projeye özel bazı bilgileri girmeniz gerekiyor: proje adı ve yolu, şirket etki alanı vb. Aşağıdaki resme bir göz atın.
Proje adını, Flutter SDK yolunu, proje konumunu ve isteğe bağlı proje açıklamasını ekleyin. İleri'ye basın.
Her uygulama (Android veya iOS olsun) benzersiz bir paket adı gerektirir. Tipik olarak, web sitenizin etki alanının tersini kullanırsınız; örneğin, com.google veya com.yahoo. Çalışan bir Flutter uygulaması oluşturmak için Bitir'e basın.
Proje oluşturulduktan sonra yukarıda gösterilen ekranı görmelisiniz. main.dart dosyasını açın (ekran görüntüsünde vurgulanmıştır). Bu ana uygulama dosyasıdır. Örnek proje kendi içinde tamamlandı ve herhangi bir değişiklik yapılmadan doğrudan bir emülatör veya fiziksel bir cihaz üzerinde çalıştırılabilir.
main.dart dosyasının içeriğini aşağıdaki kod parçacığıyla değiştirin:
import 'package:animated_widgets/FirstPage.dart'; import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Animated Widgets', debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, accentColor: Colors.redAccent, ), home: FirstPage(), ); } }
Bu kod, yalnızca yeni bir uygulama oluşturmaya ilişkin basit bilgileri ekleyerek main.dart dosyasını temizler. MyApp sınıfı bir nesne döndürür: Materyal Tasarımına uygun uygulamalar oluşturmak için temel yapıyı sağlayan bir MaterialApp
widget'ı. Kodu daha yapılandırılmış hale getirmek için lib klasörü içinde iki yeni dart dosyası oluşturun: FirstPage.dart ve Quotes.dart .
FirstPage.dart , Quoted uygulamamız için gereken tüm görsel öğelerden (widget'lar) sorumlu tüm kodu içerecektir. Tüm animasyonlar bu dosyada işlenir.
Not : Makalenin ilerleyen bölümlerinde, her animasyonlu pencere bileşeni için tüm kod parçacıkları, bu dosyaya İskele pencere aracının alt öğeleri olarak eklenir. Daha fazla bilgi için GitHub'daki bu örnek faydalı olabilir.
FirstPage.dart'a aşağıdaki kodu ekleyerek başlayın. Bu, daha sonra başka şeylerin ekleneceği kısmi koddur.
import 'dart:math'; import 'package:animated_widgets/Quotes.dart'; import 'package:flutter/material.dart'; class FirstPage extends StatefulWidget { @override State createState() { return FirstPageState(); } } class FirstPageState extends State with TickerProviderStateMixin { bool showNextButton = false; bool showNameLabel = false; bool alignTop = false; bool increaseLeftPadding = false; bool showGreetings = false; bool showQuoteCard = false; String name = ''; double screenWidth; double screenHeight; String quote; @override void initState() { super.initState(); Random random = new Random(); int quoteIndex = random.nextInt(Quotes.quotesArray.length); quote = Quotes.quotesArray[quoteIndex]; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: [ // All other children will be added here. // In this article, all the children widgets are contained // in their own separate methods. // Just method calls should be added here for the respective child. ], ), ); } }
import 'dart:math'; import 'package:animated_widgets/Quotes.dart'; import 'package:flutter/material.dart'; class FirstPage extends StatefulWidget { @override State createState() { return FirstPageState(); } } class FirstPageState extends State with TickerProviderStateMixin { bool showNextButton = false; bool showNameLabel = false; bool alignTop = false; bool increaseLeftPadding = false; bool showGreetings = false; bool showQuoteCard = false; String name = ''; double screenWidth; double screenHeight; String quote; @override void initState() { super.initState(); Random random = new Random(); int quoteIndex = random.nextInt(Quotes.quotesArray.length); quote = Quotes.quotesArray[quoteIndex]; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: [ // All other children will be added here. // In this article, all the children widgets are contained // in their own separate methods. // Just method calls should be added here for the respective child. ], ), ); } }
import 'dart:math'; import 'package:animated_widgets/Quotes.dart'; import 'package:flutter/material.dart'; class FirstPage extends StatefulWidget { @override State createState() { return FirstPageState(); } } class FirstPageState extends State with TickerProviderStateMixin { bool showNextButton = false; bool showNameLabel = false; bool alignTop = false; bool increaseLeftPadding = false; bool showGreetings = false; bool showQuoteCard = false; String name = ''; double screenWidth; double screenHeight; String quote; @override void initState() { super.initState(); Random random = new Random(); int quoteIndex = random.nextInt(Quotes.quotesArray.length); quote = Quotes.quotesArray[quoteIndex]; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: [ // All other children will be added here. // In this article, all the children widgets are contained // in their own separate methods. // Just method calls should be added here for the respective child. ], ), ); } }
import 'dart:math'; import 'package:animated_widgets/Quotes.dart'; import 'package:flutter/material.dart'; class FirstPage extends StatefulWidget { @override State createState() { return FirstPageState(); } } class FirstPageState extends State with TickerProviderStateMixin { bool showNextButton = false; bool showNameLabel = false; bool alignTop = false; bool increaseLeftPadding = false; bool showGreetings = false; bool showQuoteCard = false; String name = ''; double screenWidth; double screenHeight; String quote; @override void initState() { super.initState(); Random random = new Random(); int quoteIndex = random.nextInt(Quotes.quotesArray.length); quote = Quotes.quotesArray[quoteIndex]; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: [ // All other children will be added here. // In this article, all the children widgets are contained // in their own separate methods. // Just method calls should be added here for the respective child. ], ), ); } }
Quotes.dart dosyası, tüm sabit kodlanmış alıntıların bir listesini içerir. Burada dikkat edilmesi gereken bir nokta, listenin statik bir nesne olmasıdır. Bu, Quotes sınıfının yeni bir nesnesini yaratmadan başka yerlerde kullanılabileceği anlamına gelir. Yukarıdaki liste sadece bir yardımcı program olarak hareket ettiğinden, bu tasarım tarafından seçilir.
Bu dosyaya aşağıdaki kodu ekleyin:
class Quotes { static const quotesArray = [ "Good, better, best. Never let it rest. 'Til your good is better and your better is best", "It does not matter how slowly you go as long as you do not stop.", "Only I can change my life. No one can do it for me." ]; }
Proje iskeleti artık hazır, o yüzden alıntıyı biraz daha detaylandıralım.
AnimatedOpacity
Uygulamaya kişisel bir dokunuş katmak için kullanıcının adını bilmek güzel olurdu, o yüzden isteyelim ve bir sonraki düğmesi gösterelim. Kullanıcı adını girene kadar bu buton gizlidir ve bir isim verildiğinde zarif bir şekilde görünecektir. Düğme için bir tür görünürlük animasyonuna ihtiyacımız var, ancak bunun için bir widget var mı? Evet var.
AnimatedOpacity
girin. Bu pencere öğesi, örtük animasyon desteği ekleyerek Opaklık pencere aracını temel alır. Nasıl kullanırız? Senaryomuzu hatırlayın: animasyonlu görünürlük ile bir sonraki düğmesi göstermemiz gerekiyor. Düğme pencere öğesini AnimatedOpacity
pencere aracının içine sarıyoruz, bazı uygun değerlerle besliyoruz ve animasyonu tetiklemek için bir koşul ekliyoruz - ve Flutter gerisini halledebilir.
_getAnimatedOpacityButton() { return AnimatedOpacity( duration: Duration(seconds: 1), reverseDuration: Duration(seconds: 1), curve: Curves.easeInOut, opacity: showNextButton ? 1 : 0, child: _getButton(), ); }
AnimatedOpacity
pencere aracının iki zorunlu özelliği vardır:
-
opacity
1 değeri tamamen görünür anlamına gelir; 0 (sıfır) gizli anlamına gelir. Flutter, canlandırırken bu iki uç nokta arasındaki değerleri enterpolasyon yapar. Görünürlüğü değiştirmek için bir koşulun nasıl yerleştirildiğini görebilir, böylece animasyonu tetikleyebilirsiniz. -
child
Görünürlüğünü canlandıracak alt pencere öğesi.
Artık örtük widget ile görünürlük animasyonu eklemenin ne kadar basit olduğunu anlamalısınız. Ve tüm bu widget'lar aynı yönergeleri takip eder ve kullanımı kolaydır. Bir sonrakine geçelim.
AnimatedCrossFade
Kullanıcının adını biliyoruz, ancak widget hala giriş bekliyor. Bir önceki adımda kullanıcı adını girerken sonraki butonunu görüntülüyoruz. Şimdi, kullanıcı düğmeye bastığında, girişi kabul etmeyi durdurmak ve girilen adı göstermek istiyorum. Elbette bunu yapmanın birçok yolu vardır, ancak belki de girdi pencere aracını gizleyebilir ve düzenlenemez bir metin pencere aracını gösterebiliriz. AnimatedCrossFade
widget'ını kullanarak deneyelim.
Bu pencere aracı, bazı koşullara bağlı olarak aralarında çapraz geçiş yaptığı için iki alt öğe gerektirir. Bu widget'ı kullanırken akılda tutulması gereken önemli bir şey, her iki çocuğun da aynı genişlikte olması gerektiğidir. Yükseklik farklıysa, daha uzun widget alttan kırpılır. Bu senaryoda, alt öğe olarak iki pencere öğesi kullanılacaktır: giriş ve etiket.
_getAnimatedCrossfade() { return AnimatedCrossFade( duration: Duration(seconds: 1), alignment: Alignment.center, reverseDuration: Duration(seconds: 1), firstChild: _getNameInputWidget(), firstCurve: Curves.easeInOut, secondChild: _getNameLabelWidget(), secondCurve: Curves.easeInOut, crossFadeState: showNameLabel ? CrossFadeState.showSecond : CrossFadeState.showFirst, ); }
Bu pencere öğesi, farklı bir dizi zorunlu parametre gerektirir:
-
crossFadeState
Bu durum hangi çocuğun gösterileceğini belirler. -
firstChild
Bu widget için ilk alt öğeyi belirtir. -
secondChild
İkinci çocuğu belirtir.
AnimatedAlign
Bu noktada isim etiketi ekranın ortasına konumlandırılır. Alıntıları göstermek için ekranın ortasına ihtiyacımız olduğundan, üstte çok daha iyi görünecek. Basitçe söylemek gerekirse, ad etiketi widget'ının hizalaması merkezden üste değiştirilmelidir. Ve önceki çapraz geçiş animasyonuyla birlikte bu hizalama değişikliğini canlandırmak güzel olmaz mıydı? Haydi Yapalım şunu.
Her zaman olduğu gibi, bunu başarmak için birkaç teknik kullanılabilir. Ad etiketi widget'ı zaten merkeze hizalanmış olduğundan, hizalamasını canlandırmak, widget'ın üst ve sol değerlerini değiştirmekten çok daha basit olacaktır. AnimatedAlign
widget'ı bu iş için mükemmel.
Bu animasyonu başlatmak için bir tetikleyici gereklidir. Bu pencere öğesinin tek amacı, hizalama değişikliğini canlandırmaktır, bu nedenle yalnızca birkaç özelliği vardır: bir alt öğe ekleyin, hizalamasını ayarlayın, hizalama değişikliğini tetikleyin ve hepsi bu kadar.
_getAnimatedAlignWidget() { return AnimatedAlign( duration: Duration(seconds: 1), curve: Curves.easeInOut, alignment: alignTop ? Alignment.topLeft : Alignment.center, child: _getAnimatedCrossfade(), ); }
Yalnızca iki zorunlu özelliği vardır:
- çocuk:
Hizalaması değiştirilecek olan çocuk. - hizalama:
Gerekli hizalama değeri.
Bu widget gerçekten basit ama sonuçlar zarif. Ayrıca, daha karmaşık bir animasyon oluşturmak için iki farklı animasyonlu widget'ı ne kadar kolay kullanabileceğimizi gördük. Bu, animasyonlu widget'ların güzelliğidir.
AnimatedPadding
Artık, farklı türde animasyonlu widget'lar kullanarak, fazla çaba harcamadan sorunsuz bir şekilde canlandırılan kullanıcının adı en üstte. İsmin önüne bir “Merhaba” selamı ekleyelim. En üstte "Merhaba" değerine sahip bir metin widget'ı eklemek, onu aşağıdaki resimdeki gibi görünen karşılama metni widget'ı ile örtüştürecektir.
Ad metni widget'ının solunda biraz dolgu varsa ne olur? Sol dolguyu artırmak kesinlikle işe yarayacaktır, ancak bekleyin: dolguyu biraz animasyonla artırabilir miyiz? Evet, AnimatedPadding
yaptığı da budur. Tüm bunların daha iyi görünmesini sağlamak için, aynı anda selamlama metni widget'ının solmasını ve ad metni widget'ının dolgusunun artmasını sağlayalım.
_getAnimatedPaddingWidget() { return AnimatedPadding( duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn, padding: increaseLeftPadding ? EdgeInsets.only(left: 28.0) : EdgeInsets.only(left: 0), child: _getAnimatedCrossfade(), ); }
Yukarıdaki animasyonun yalnızca önceki animasyon hizalaması tamamlandıktan sonra gerçekleşmesi gerektiğinden, bu animasyonu tetiklemeyi geciktirmemiz gerekiyor. Konudan kısaca uzaklaşarak, gecikme eklemek için popüler bir mekanizma hakkında konuşmak için iyi bir an. Flutter bu tür birkaç teknik sağlar, ancak Future.delayed yapıcısı daha basit, daha temiz ve daha okunabilir yaklaşımlardan biridir. Örneğin, 1 saniye sonra bir kod parçasını çalıştırmak için:
Future.delayed(Duration(seconds: 1), (){ sum = a + b; // This sum will be calculated after 1 second. print(sum); });
Gecikme süresi zaten bilindiğinden (önceki animasyon sürelerinden hesaplanmıştır), animasyon bu aralıktan sonra tetiklenebilir.
// Showing “Hi” after 1 second - greetings visibility trigger. _showGreetings() { Future.delayed(Duration(seconds: 1), () { setState(() { showGreetings = true; }); }); } // Increasing the padding for name label widget after 1 second - increase padding trigger. _increaseLeftPadding() { Future.delayed(Duration(seconds: 1), () { setState(() { increaseLeftPadding = true; }); }); }
Bu widget'ın yalnızca iki zorunlu özelliği vardır:
-
child
Bu widget'ın içindeki, dolgunun uygulanacağı çocuk. -
padding
Eklenecek alan miktarı.
AnimatedSize
Bugün, bir tür animasyona sahip herhangi bir uygulama, kullanıcının dikkatini çekmek için görsel bileşenleri yakınlaştırmayı veya uzaklaştırmayı içerecektir (genellikle ölçeklendirme animasyonu olarak adlandırılır). Neden aynı tekniği burada kullanmıyorsunuz? Kullanıcıya ekranın ortasından yakınlaştıran motive edici bir alıntı gösterebiliriz. Size, çocuğunun boyutunu değiştirerek kontrol edilen yakınlaştırma ve uzaklaştırma efektlerini sağlayan AnimatedSize
widget'ını tanıtmama izin verin.
Bu widget, gerekli parametreler söz konusu olduğunda diğerlerinden biraz farklıdır. Flutter'ın "Ticker" dediği şeye ihtiyacımız var. Flutter, nesnelerin yeni bir çerçeve olayı tetiklendiğinde bunu bilmesini sağlayan bir yönteme sahiptir. “Şimdi yap! … Şimdi yap! … Şimdi yap! …”
AnimatedSize
pencere aracı, bir ticker sağlayıcısını kabul eden bir vsync özelliği gerektirir. Bir borsa sağlayıcısı edinmenin en kolay yolu, sınıfa bir Mixin eklemektir. İki temel kayan nokta sağlayıcı uygulaması vardır: SingleTickerProviderStateMixin
, tek bir kayan nokta sağlayan; ve birkaç sağlayan TickerProviderStateMixin
.
Bir Ticker'ın varsayılan uygulaması, bir animasyonun karelerini işaretlemek için kullanılır. Bu durumda, ikincisi kullanılır. Karışımlar hakkında daha fazla bilgi.
// Helper method to create quotes card widget. _getQuoteCardWidget() { return Card( color: Colors.green, elevation: 8.0, child: _getAnimatedSizeWidget(), ); } // Helper method to create animated size widget and set its properties. _getAnimatedSizeWidget() { return AnimatedSize( duration: Duration(seconds: 1), curve: Curves.easeInOut, vsync: this, child: _getQuoteContainer(), ); } // Helper method to create the quotes container widget with different sizes. _getQuoteContainer() { return Container( height: showQuoteCard ? 100 : 0, width: showQuoteCard ? screenWidth - 32 : 0, child: Center( child: Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: Text(quote, style: TextStyle(color: Colors.white, fontWeight: FontWeight.w400, fontSize: 14),), ), ), ); } // Trigger used to show the quote card widget. _showQuote() { Future.delayed(Duration(seconds: 2), () { setState(() { showQuoteCard = true; }); }); }
Bu widget için zorunlu özellikler:
-
vsync
Animasyonu ve çerçeve değişikliklerini koordine etmek için gerekli ticker sağlayıcısı.< -
child
Bedeni değişen çocuk canlandırılacaktır.
Yakınlaştırma ve uzaklaştırma animasyonu artık kolayca evcilleştirildi.
AnimatedPositioned
Harika! Tırnaklar, kullanıcının dikkatini çekmek için merkezden yakınlaştırır. Yakınlaştırırken alttan yukarı kayarsa ne olur? Hadi deneyelim. Bu hareket, alıntı widget'ının konumuyla oynamayı ve konum özelliklerindeki değişiklikleri canlandırmayı içerir. AnimatedPositioned
mükemmel bir adaydır.
Bu pencere öğesi, belirtilen konum değiştiğinde, çocuğun konumunu belirli bir süre boyunca otomatik olarak değiştirir. Unutulmaması gereken bir nokta: yalnızca ana widget'ı bir "Yığın" ise çalışır. Bu widget oldukça basit ve kullanımı kolaydır. Bakalım.
// Helper method to create the animated positioned widget. // With position changes based on “showQuoteCard” flag. _getAnimatedPositionWidget() { return AnimatedPositioned( duration: Duration(seconds: 1), curve: Curves.easeInOut, child: _getQuoteCardWidget(), top: showQuoteCard ? screenHeight/2 - 100 : screenHeight, left: !showQuoteCard ? screenWidth/2 : 12, ); }
Bu widget'ın yalnızca bir zorunlu özelliği vardır:
-
child
Konumu değiştirilecek olan widget.
Çocuğun boyutunun konumuyla birlikte değişmesi beklenmiyorsa, bu widget'a daha performanslı bir alternatif SlideTransition
olacaktır.
İşte tam animasyonumuz:
Çözüm
Animasyonlar, kullanıcı deneyiminin ayrılmaz bir parçasıdır. Hareketli animasyonlu statik uygulamalar veya uygulamalar, yalnızca kullanıcıyı elde tutma oranını düşürmekle kalmaz, aynı zamanda bir geliştiricinin sonuç sunma itibarını da düşürür.
Günümüzde çoğu popüler uygulama, kullanıcıları memnun etmek için bir tür ince animasyona sahiptir. Kullanıcı isteklerine yönelik animasyonlu geri bildirim, onları daha fazlasını keşfetmeye teşvik edebilir. Flutter, sorunsuz ve duyarlı animasyonlar için zengin destek de dahil olmak üzere platformlar arası geliştirme için birçok özellik sunar.
Flutter, diğer geliştiricilerin animasyonlarını kullanmamıza izin veren harika bir eklenti desteğine sahiptir. Şimdi topluluktan çok fazla sevgi ile 1.9 sürümüne olgunlaştığına göre, Flutter gelecekte daha iyi olmaya mahkumdur. Şimdi Flutter öğrenmek için harika bir zaman olduğunu söyleyebilirim!
Diğer Kaynaklar
- "Animasyon ve Hareket Widget'ları" Flutter Docs
- "Animasyonlara Giriş" Flutter Docs
- Flutter Codelab'leri
Editörün Notu : Bu makaleyi gözden geçirmedeki yardımları için Ahmad Awais'e çok teşekkür ederim.