Animarea aplicațiilor cu Flutter
Despre autor
Un dezvoltator de software full stack în timpul zilei și dezvoltator de aplicații mobile în timpul nopții. În timp ce nu lucrez, îmi place să gătesc și să mă joc jocuri pe PC.Mai multe despreShubham↬
- 14 min citește
- Aplicații,Animație,
- Salvate pentru citire offline
- Share on Twitter, LinkedIn
Aplicațiile pentru orice platformă sunt lăudate atunci când sunt intuitive, arătoase și oferă un feedback plăcut la interacțiunile utilizatorilor. Animația este una dintre modalitățile de a face exact acest lucru.
Flutter, un framework cross-platform, s-a maturizat în ultimii doi ani pentru a include suport web și desktop. Și-a dobândit reputația că aplicațiile dezvoltate cu el sunt fluide și arătoase. Cu suportul său bogat pentru animații, modul declarativ de scriere a interfeței de utilizator, „Hot Reload” și alte caracteristici, acesta este acum un framework cross-platform complet.
Dacă sunteți la început cu Flutter și doriți să învățați un mod neconvențional de a adăuga animații, atunci sunteți la locul potrivit: vom explora domeniul animațiilor și al widget-urilor de mișcare, un mod implicit de adăugare a animațiilor.
Flutter se bazează pe conceptul de widget-uri. Fiecare componentă vizuală a unei aplicații este un widget – gândiți-vă la ele ca la vizualizările din Android. Flutter oferă suport pentru animații folosind o clasă Animation, un obiect „AnimationController” pentru gestionare și „Tween” pentru a interpola intervalul de date. Aceste trei componente lucrează împreună pentru a oferi o animație lină. Deoarece acest lucru necesită crearea și gestionarea manuală a animației, este cunoscut ca un mod explicit de animație.
Acum permiteți-mi să vă prezint animația și widgeturile de mișcare. Flutter oferă numeroase widget-uri care suportă în mod inerent animația. Nu este nevoie să creați un obiect de animație sau vreun controler, deoarece toată animația este gestionată de această categorie de widgeturi. Trebuie doar să alegeți widgetul adecvat pentru animația necesară și să treceți valorile proprietăților widgetului pentru a anima. Această tehnică este o modalitate implicită de animare.
Graficul de mai sus stabilește în linii mari ierarhia animațiilor în Flutter, modul în care sunt acceptate atât animațiile explicite, cât și cele implicite.
Câteva dintre widget-urile animate acoperite în acest articol sunt:
AnimatedOpacity
AnimatedCrossFade
AnimatedAlign
AnimatedPadding
AnimatedSize
-
AnimatedPositioned
.
Flutter nu oferă doar widget-uri animate predefinite, ci și un widget generic numit AnimatedWidget
, care poate fi utilizat pentru a crea widget-uri animate implicit personalizate. După cum reiese din nume, aceste widget-uri aparțin categoriei de widget-uri animate și de mișcare și, prin urmare, au câteva proprietăți comune care ne permit să realizăm animații mult mai fluide și mai arătoase.
Dați-mi voie să explic acum aceste proprietăți comune, deoarece ele vor fi folosite mai târziu în toate exemplele.
-
duration
Durata pe care se vor anima parametrii. -
reverseDuration
Durata animației inverse. -
curve
Curba care se va aplica la animarea parametrilor. Valorile interpolate pot fi preluate dintr-o distribuție liniară sau, dacă și când este specificat, pot fi preluate dintr-o curbă.
Să începem călătoria prin crearea unei aplicații simple pe care o vom numi „Quoted”. Aceasta va afișa un citat aleatoriu de fiecare dată când aplicația va porni. Două lucruri de reținut: în primul rând, toate aceste citate vor fi hardcodate în aplicație; și în al doilea rând, nu vor fi salvate date ale utilizatorului.
Nota: Toate fișierele pentru aceste exemple pot fi găsite pe GitHub.
Getting Started
Flutter ar trebui să fie instalat și veți avea nevoie de o anumită familiaritate cu fluxul de bază înainte de a trece mai departe. Un loc bun pentru a începe este, „Using Google’s Flutter For Truly Cross-Platform Mobile Development”.
Crearea unui nou proiect Flutter în Android Studio.
Aceasta va deschide un nou asistent de proiect, unde puteți configura elementele de bază ale proiectului.
În ecranul de selectare a tipului de proiect, există diferite tipuri de proiecte Flutter, fiecare dintre ele răspunzând unui scenariu specific… Pentru acest tutorial, alegeți Flutter Application și apăsați Next.
Acum trebuie să introduceți câteva informații specifice proiectului: numele și calea proiectului, domeniul companiei și așa mai departe. Aruncați o privire la imaginea de mai jos.
Adaugați numele proiectului, calea SDK Flutter, locația proiectului și o descriere opțională a proiectului. Apăsați Next.
Care aplicație (fie ea Android sau iOS) necesită un nume de pachet unic. De obicei, se folosește inversul domeniului site-ului dvs. web; de exemplu, com.google sau com.yahoo. Apăsați Finish (Terminare) pentru a genera o aplicație Flutter funcțională.
După ce proiectul este generat, ar trebui să vedeți ecranul prezentat mai sus. Deschideți fișierul main.dart (evidențiat în captura de ecran). Acesta este fișierul principal al aplicației. Proiectul de probă este complet în sine și poate fi rulat direct pe un emulator sau pe un dispozitiv fizic fără nicio modificare.
Înlocuiți conținutul fișierului main.dart cu următorul fragment de cod:
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(), ); }}
Acest cod curăță fișierul main.dart prin simpla adăugare de informații simple relevante pentru crearea unei noi aplicații. Clasa MyApp returnează un obiect: un widget MaterialApp
, care oferă structura de bază pentru crearea de aplicații conforme cu Material Design. Pentru a face codul mai structurat, creați două fișiere dart noi în interiorul folderului lib: FirstPage.dart și Quotes.dart.
FirstPage.dart va conține tot codul responsabil pentru toate elementele vizuale (widget-uri) necesare pentru aplicația noastră Quoted. Toată animația este gestionată în acest fișier.
Nota: Mai târziu în articol, toate fragmentele de cod pentru fiecare widget animat sunt adăugate în acest fișier ca și copii ai widgetului Scaffold. Pentru mai multe informații, Acest exemplu de pe GitHub ar putea fi util.
Începeți prin a adăuga următorul cod la FirstPage.dart. Acesta este codul parțial, unde alte lucruri vor fi adăugate mai târziu.
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; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: , ), ); }}
Fisierul Quotes.dart conține o listă cu toate citatele codificate în mod greșit. Un aspect care trebuie remarcat aici este că lista este un obiect static. Acest lucru înseamnă că poate fi utilizat în alte locuri fără a crea un nou obiect din clasa Quotes. Acest lucru este ales în mod intenționat, deoarece lista de mai sus acționează pur și simplu ca un utilitar.
Adaugați următorul cod la acest fișier:
class Quotes { static const quotesArray = ;}
Scheletul proiectului este acum gata, așa că haideți să dezvoltăm puțin mai mult Citat.
AnimatedOpacity
Pentru a da o notă personală aplicației, ar fi frumos să cunoaștem numele utilizatorului, așa că haideți să îl cerem și să arătăm un buton următor. Până când utilizatorul își introduce numele, acest buton este ascuns, iar acesta va apărea în mod grațios atunci când este dat un nume. Avem nevoie de un fel de animație de vizibilitate pentru buton, dar există vreun widget pentru asta? Da, există.
Intră AnimatedOpacity
. Acest widget se bazează pe widgetul Opacitate prin adăugarea suportului implicit de animație. Cum îl folosim? Amintiți-vă scenariul nostru: trebuie să afișăm un buton următor cu vizibilitate animată. Înfășurăm widget-ul buton în interiorul widget-ului AnimatedOpacity
, introducem niște valori adecvate și adăugăm o condiție pentru a declanșa animația – iar Flutter se poate ocupa de restul.
_getAnimatedOpacityButton() { return AnimatedOpacity( duration: Duration(seconds: 1), reverseDuration: Duration(seconds: 1), curve: Curves.easeInOut, opacity: showNextButton ? 1 : 0, child: _getButton(), );}
Widgetul AnimatedOpacity
are două proprietăți obligatorii:
-
opacity
O valoare de 1 înseamnă complet vizibil; 0 (zero) înseamnă ascuns. În timpul animației, Flutter interpolează valorile între aceste două extreme. Puteți vedea cum este plasată o condiție pentru a schimba vizibilitatea, declanșând astfel animația. -
child
Widgetul copil căruia îi va fi animată vizibilitatea.
Ar trebui să înțelegeți acum cât de simplu este să adăugați animația vizibilității cu ajutorul widgetului implicit. Și toate aceste widgeturi respectă aceleași linii directoare și sunt ușor de utilizat. Să trecem la următorul.
AnimatedCrossFade
Avem numele utilizatorului, dar widget-ul încă așteaptă să fie introdus. În pasul anterior, pe măsură ce utilizatorul își introduce numele, afișăm butonul următor. Acum, atunci când utilizatorul apasă pe buton, vreau să nu mai acceptăm intrări și să afișăm numele introdus. Există mai multe moduri de a face acest lucru, desigur, dar poate că putem ascunde widgetul de introducere și afișa un widget de text needitabil. Să încercăm acest lucru folosind widgetul AnimatedCrossFade
.
Acest widget necesită doi copii, deoarece widgetul trece de la unul la altul în funcție de o anumită condiție. Un lucru important de reținut în timpul utilizării acestui widget este că ambii copii trebuie să aibă aceeași lățime. Dacă înălțimea este diferită, atunci widgetul mai înalt va fi decupat din partea de jos. În acest scenariu, două widget-uri vor fi folosite ca și copii: input și label.
_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, );}
Acest widget necesită un set diferit de parametri obligatorii:
-
crossFadeState
Acest stat calculează ce copil se va afișa. -
firstChild
Specifică primul copil pentru acest widget. -
secondChild
Specifică al doilea copil.
AnimatedAlign
În acest moment, eticheta cu numele este poziționată în centrul ecranului. Va arăta mult mai bine în partea de sus, deoarece avem nevoie de centrul ecranului pentru a afișa ghilimele. Pur și simplu, alinierea widget-ului etichetei de nume trebuie schimbată din centru în partea de sus. Și nu ar fi frumos să animăm această schimbare de aliniere împreună cu animația cross-fade anterioară? Haideți să o facem.
Ca întotdeauna, pot fi folosite mai multe tehnici pentru a realiza acest lucru. Din moment ce widget-ul etichetă cu nume este deja aliniat central, animarea alinierii sale ar fi mult mai simplă decât manipularea valorilor de sus și de stânga ale widget-ului. Widgetul AnimatedAlign
este perfect pentru această sarcină.
Pentru a iniția această animație, este necesar un declanșator. Singurul scop al acestui widget este de a anima schimbarea alinierii, așa că are doar câteva proprietăți: adăugați un copil, setați-i alinierea, declanșați schimbarea alinierii și asta este tot.
_getAnimatedAlignWidget() { return AnimatedAlign(duration: Duration(seconds: 1),curve: Curves.easeInOut,alignment: alignTop ? Alignment.topLeft : Alignment.center,child: _getAnimatedCrossfade(), );}
Aceasta are doar două proprietăți obligatorii:
- copil:
Copilul a cărui aliniere va fi modificată. - aliniere:
Valoare cerută pentru aliniere.
Acest widget este foarte simplu, dar rezultatele sunt elegante. În plus, am văzut cât de ușor putem folosi două widget-uri animate diferite pentru a crea o animație mai complexă. Aceasta este frumusețea widgeturilor animate.
AnimatedPadding
Acum avem numele utilizatorului în partea de sus, animat lin, fără prea mult efort, folosind diferite tipuri de widgeturi animate. Să adăugăm un salut, „Salut”, înainte de nume. Adăugarea unui widget text cu valoarea „Hi,” în partea de sus va face ca acesta să se suprapună peste widgetul text de salut, arătând ca în imaginea de mai jos.
Ce s-ar întâmpla dacă widgetul de text nume ar avea niște umplutură în stânga? Mărirea padding-ului din stânga va funcționa cu siguranță, dar stați puțin: putem mări padding-ul cu ceva animație? Da, și asta este ceea ce face AnimatedPadding
. Pentru ca totul să arate mult mai bine, haideți să facem ca widget-ul text de salut să se estompeze și ca umplutura widget-ului text de nume să crească în același timp.
_getAnimatedPaddingWidget() { return AnimatedPadding( duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn, padding: increaseLeftPadding ? EdgeInsets.only(left: 28.0) : EdgeInsets.only(left: 0), child: _getAnimatedCrossfade(), );}
Din moment ce animația de mai sus ar trebui să apară numai după ce alinierea animată anterioară este completă, trebuie să amânăm declanșarea acestei animații. Făcând o scurtă digresiune de la subiect, acesta este un moment bun pentru a vorbi despre un mecanism popular de adăugare a întârzierii. Flutter oferă mai multe astfel de tehnici, dar constructorul Future.delayed este una dintre abordările mai simple, mai curate și mai ușor de citit. De exemplu, pentru a executa o bucată de cod după 1 secundă:
Future.delayed(Duration(seconds: 1), (){ sum = a + b; // This sum will be calculated after 1 second. print(sum);});
Din moment ce durata de întârziere este deja cunoscută (calculată din duratele animațiilor anterioare), animația poate fi declanșată după acest interval.
// 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; }); });}
Acest widget are doar două proprietăți obligatorii:
-
child
Copilul din interiorul acestui widget, căruia i se va aplica padding. -
padding
Cantitatea de spațiu de adăugat.
AnimatedSize
În prezent, orice aplicație care are un fel de animație va include mărirea sau micșorarea componentelor vizuale pentru a capta atenția utilizatorului (numită în mod obișnuit animație de scalare). De ce să nu folosim aceeași tehnică și aici? Putem să arătăm utilizatorului un citat motivațional care face zoom in din centrul ecranului. Permiteți-mi să vă prezint widget-ul AnimatedSize
, care activează efectele de mărire și micșorare, controlate prin modificarea dimensiunii copilului său.
Acest widget este puțin diferit de celelalte când vine vorba de parametrii necesari. Avem nevoie de ceea ce Flutter numește un „Ticker”. Flutter are o metodă pentru a anunța obiectele ori de câte ori este declanșat un eveniment de cadru nou. Aceasta poate fi gândită ca ceva care trimite un semnal care spune: „Fă-o acum! … Fă-o acum! … Fă-o acum! …”
Widgetul AnimatedSize
necesită o proprietate – vsync – care acceptă un furnizor de ticker. Cel mai simplu mod de a obține un furnizor de ticker este să adăugați un Mixin la clasă. Există două implementări de bază ale furnizorului de ticker: SingleTickerProviderStateMixin
, care oferă un singur ticker; și TickerProviderStateMixin
, care oferă mai multe.
Implementarea implicită a unui Ticker este utilizată pentru a marca cadrele unei animații. În acest caz, aceasta din urmă este utilizată. Mai multe despre mixins.
// 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; }); });}
Proprietăți obligatorii pentru acest widget:
-
vsync
Furnizorul de ticker necesar pentru a coordona animația și schimbările de cadre.< -
child
Copilul ale cărui schimbări de mărime vor fi animate.
Animația zoom in și zoom out este acum ușor de îmblânzit.
AnimatedPositioned
Genial! Citatele se apropie din centru pentru a capta atenția utilizatorului. Ce-ar fi dacă ar aluneca de jos în sus în timp ce se face zoom in? Haideți să încercăm. Această mișcare presupune să ne jucăm cu poziția widget-ului de citate și să animăm modificările proprietăților de poziție. AnimatedPositioned
este candidatul perfect.
Acest widget face automat tranziția poziției copilului pe o anumită durată ori de câte ori se schimbă poziția specificată. Un aspect de reținut: funcționează numai dacă widgetul său părinte este un „Stack”. Acest widget este destul de simplu și direct de utilizat. Să vedem.
// 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, );}
Acest widget are o singură proprietate obligatorie:
-
child
Widgetul a cărui poziție va fi modificată.
Dacă nu se așteaptă ca dimensiunea copilului să se schimbe împreună cu poziția sa, o alternativă mai performativă la acest widget ar fi SlideTransition
.
Iată animația noastră completă:
Concluzie
Animațiile sunt o parte integrantă a experienței utilizatorului. Aplicațiile statice sau aplicațiile cu animații nesigure nu numai că scad retenția utilizatorilor, dar și reputația unui dezvoltator de a livra rezultate.
Astăzi, majoritatea aplicațiilor populare au un fel de animație subtilă pentru a încânta utilizatorii. Un feedback animat la solicitările utilizatorilor îi poate, de asemenea, determina pe aceștia să exploreze mai mult. Flutter oferă o mulțime de caracteristici pentru dezvoltarea multi-platformă, inclusiv un suport bogat pentru animații fluide și receptive.
Flutter are un suport excelent pentru plug-in-uri care ne permite să folosim animații de la alți dezvoltatori. Acum că s-a maturizat până la versiunea 1.9, cu atât de multă dragoste din partea comunității, Flutter va deveni cu siguranță mai bun în viitor. Aș spune că acum este un moment excelent pentru a învăța Flutter!
.
Leave a Reply