Animering af apps med Flutter
![Shubham](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/14afc623-f044-4534-9412-a83e8cef990a/shubham-200px.jpg)
Om forfatteren
En full stack softwareudvikler om dagen og udvikler af mobile apps om natten. Når jeg ikke arbejder, elsker jeg at lave mad og spille pc-spil.Mere omShubham↬
- 14 min læsning
- Apps,Animation
- Sparet til offline læsning
- Del på Twitter, LinkedIn
Apps til enhver platform bliver rost, når de er intuitive, ser godt ud og giver behagelig feedback på brugerinteraktioner. Animation er en af måderne at gøre netop dette på.
Flutter, en ramme på tværs af platforme, er i de seneste to år modnet til at omfatte understøttelse af web og desktop. Det har fået et ry for, at apps, der er udviklet med det, er smidige og flotte. Med sin rige animationsunderstøttelse, deklarative måde at skrive brugergrænseflader på, “Hot Reload” og andre funktioner er det nu en komplet ramme på tværs af platforme.
Hvis du starter med Flutter og ønsker at lære en utraditionel måde at tilføje animation på, er du kommet til det rette sted: Vi vil udforske området animation og motion widgets, en implicit måde at tilføje animationer på.
Flutter er baseret på konceptet widgets. Hver visuel komponent i en app er en widget – tænk på dem som visninger i Android. Flutter giver understøttelse af animation ved hjælp af en Animation-klasse, et “AnimationController”-objekt til styring og “Tween” til at interpolere dataområdet. Disse tre komponenter arbejder sammen for at give en jævn animation. Da dette kræver manuel oprettelse og forvaltning af animation, er det kendt som en eksplicit måde at animere på.
Lad mig nu introducere dig til animation og bevægelseswidgets. Flutter tilbyder adskillige widgets, som i sagens natur understøtter animation. Der er ikke behov for at oprette et animationsobjekt eller en controller, da al animation håndteres af denne kategori af widgets. Du skal blot vælge den passende widget til den ønskede animation og sende widgets egenskabsværdier for at animere. Denne teknik er en implicit måde at animere på.
![](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a5ff46b9-aa12-4268-87c7-6907471b0ef1/flutter-animation-hierarchy.png)
Diagrammet ovenfor beskriver groft sagt animationshierarkiet i Flutter, hvordan både eksplicit og implicit animation understøttes.
Nogle af de animerede widgets, der er omfattet af denne artikel, er:
AnimatedOpacity
AnimatedCrossFade
AnimatedAlign
AnimatedPadding
AnimatedSize
-
AnimatedPositioned
.
Flutter indeholder ikke kun foruddefinerede animerede widgets, men også en generisk widget kaldet AnimatedWidget
, som kan bruges til at oprette brugerdefinerede implicit animerede widgets. Som det fremgår af navnet, tilhører disse widgets kategorien animerede widgets og bevægelseswidgets, og de har derfor nogle fælles egenskaber, som gør det muligt for os at lave animationer meget glattere og flottere.
Lad mig forklare disse fælles egenskaber nu, da de vil blive brugt senere i alle eksempler.
-
duration
Den varighed, som parametrene skal animeres over. -
reverseDuration
Den omvendte animations varighed. -
curve
Den kurve, der skal anvendes ved animationen af parametrene. De interpolerede værdier kan tages fra en lineær fordeling eller, hvis og når det er angivet, kan de tages fra en kurve.
Lad os begynde rejsen ved at oprette en simpel app, som vi kalder “Citeret”. Den vil vise et tilfældigt citat, hver gang appen starter. To ting skal bemærkes: For det første vil alle disse citater blive hardcodet i applikationen, og for det andet vil der ikke blive gemt nogen brugerdata.
Notat: Alle filer til disse eksempler kan findes på GitHub.
Gå i gang
Flutter bør være installeret, og du skal have et vist kendskab til det grundlæggende flow, før du går videre. Et godt sted at starte er: “Using Google’s Flutter For Truly Cross-Platform Mobile Development”.
Opret et nyt Flutter-projekt i Android Studio.
![](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/2fa76395-f624-446c-b71f-d6b6b6a316a7/new-flutter-project.png)
Dette åbner en ny projektguide, hvor du kan konfigurere projektets grundelementer.
![](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/0d758e94-0f21-4720-807b-80ea9ae4397f/select-flutter-application.png)
I skærmbilledet til valg af projekttype er der forskellige typer Flutter-projekter, som hver især henvender sig til et specifikt scenarie… I denne vejledning skal du vælge Flutter Application og trykke på Next.
Du skal nu indtaste nogle projektspecifikke oplysninger: projektets navn og sti, virksomhedsdomæne osv. Tag et kig på billedet nedenfor.
![](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/54a602fa-8b0c-4e47-aac1-a68536549497/flutter-app-project-name.png)
Føj projektnavn, Flutter SDK-sti, projektplacering og en valgfri projektbeskrivelse. Tryk på Næste.
![](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/3184587d-3aef-4e29-8fd9-ad30e8a56dce/flutter-app-company-name.png)
Alle applikationer (uanset om det er Android eller iOS) kræver et unikt pakkenavn. Typisk bruger du det omvendte af dit websteds domæne; f.eks. com.google eller com.yahoo. Tryk på Afslut for at generere en fungerende Flutter-applikation.
![](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/aaa40006-be80-4995-bebd-b1fc4c016e1a/open-main-dart-file.png)
Når projektet er genereret, bør du se den ovenfor viste skærm. Åbn filen main.dart (fremhævet i skærmbilledet). Dette er den vigtigste programfil. Eksempelprojektet er komplet i sig selv og kan køres direkte på en emulator eller en fysisk enhed uden nogen ændring.
Udskift indholdet af filen main.dart med følgende kodestump:
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(), ); }}
Denne kode rydder op i filen main.dart ved blot at tilføje simple oplysninger, der er relevante for oprettelse af en ny app. Klassen MyApp returnerer et objekt: en MaterialApp
widget, som giver den grundlæggende struktur til oprettelse af apps, der er i overensstemmelse med Material Design. For at gøre koden mere struktureret skal du oprette to nye dart-filer i lib-mappen: FirstPage.dart og Quotes.dart.
![](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6152a6b4-f5d3-488c-8fd1-eb5fc14139b2/first-page-dart.png)
FirstPage.dart vil indeholde al den kode, der er ansvarlig for alle de visuelle elementer (widgets), der er nødvendige for vores Citat-app. Al animation håndteres i denne fil.
Bemærk: Senere i artiklen tilføjes alle kodestumperne for hver animeret widget til denne fil som børn af Scaffold-widget’en. Hvis du vil have flere oplysninger, kan dette eksempel på GitHub være nyttigt.
Start med at tilføje følgende kode til FirstPage.dart. Dette er den delvise kode, hvor andre ting vil blive tilføjet senere.
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: , ), ); }}
![](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/2b12bf2a-b80f-4cfc-b05b-a907ef130e2e/quotes-file.png)
Filen Quotes.dart indeholder en liste over alle de hårdkodede citater. Et punkt, der skal bemærkes her, er, at listen er et statisk objekt. Det betyder, at den kan bruges andre steder uden at oprette et nyt objekt af klassen Cotes. Dette er valgt med vilje, da ovenstående liste blot fungerer som et hjælpeværktøj.
Føj følgende kode til denne fil:
class Quotes { static const quotesArray = ;}
Projektskelettet er nu klar, så lad os konkretisere Citat lidt mere.
AnimatedOpacity
For at give appen et personligt præg ville det være rart at kende brugerens navn, så lad os spørge om det og vise en næste knap. Indtil brugeren indtaster sit navn, er denne knap skjult, og den vil elegant blive vist, når et navn er angivet. Vi har brug for en form for synlighedsanimation for knappen, men er der en widget til det? Ja, det er der.
Indtast AnimatedOpacity
. Denne widget bygger videre på widgetten Opacity ved at tilføje understøttelse af implicit animation. Hvordan kan vi bruge den? Husk vores scenarie: Vi har brug for at vise en næste knap med animeret synlighed. Vi pakker knapwidgetten ind i AnimatedOpacity
-widgetten, indfører nogle ordentlige værdier og tilføjer en betingelse for at udløse animationen – og Flutter kan klare resten.
_getAnimatedOpacityButton() { return AnimatedOpacity( duration: Duration(seconds: 1), reverseDuration: Duration(seconds: 1), curve: Curves.easeInOut, opacity: showNextButton ? 1 : 0, child: _getButton(), );}
![](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/607f778f-7968-4330-a565-cd5d9b818b8e/animated-opacity-400w.gif)
Den AnimatedOpacity
widget har to obligatoriske egenskaber:
-
opacity
En værdi på 1 betyder helt synlig; 0 (nul) betyder skjult. Under animationen interpolerer Flutter værdier mellem disse to ekstremer. Du kan se, hvordan en betingelse er placeret for at ændre synligheden og dermed udløse animationen. -
child
Den underordnede widget, hvis synlighed vil blive animeret.
Du burde nu forstå, hvor virkelig enkelt det er at tilføje synlighedsanimation med den implicitte widget. Og alle sådanne widgets følger de samme retningslinjer og er nemme at bruge. Lad os gå videre til den næste.
AnimatedCrossFade
Vi har brugerens navn, men widget’en venter stadig på input. I det foregående trin, når brugeren indtaster sit navn, viser vi den næste knap. Nu, når brugeren trykker på knappen, ønsker jeg at stoppe med at acceptere input og vise det indtastede navn. Der er naturligvis mange måder at gøre det på, men måske kan vi skjule input-widget’en og vise en widget med tekst, der ikke kan redigeres. Lad os prøve det med widget AnimatedCrossFade
.
Denne widget kræver to børn, da widget’en krydspasser mellem dem baseret på en eller anden betingelse. En vigtig ting at huske på, mens du bruger denne widget, er, at begge børn skal have samme bredde. Hvis højden er forskellig, vil den højere widget blive klippet fra bunden. I dette scenarie vil der blive brugt to widgets som børn: input og 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, );}
![](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/376db7c4-6934-4ef3-84b5-bc813a096b1b/animated-crossfade-400w.gif)
Denne widget kræver et andet sæt obligatoriske parametre:
-
crossFadeState
Denne tilstand finder ud af, hvilket barn der skal vises. -
firstChild
Angiver det første barn for denne widget. -
secondChild
Angiver det andet barn.
AnimatedAlign
På dette tidspunkt er navneetiketten placeret i midten af skærmen. Det vil se meget bedre ud i toppen, da vi har brug for midten af skærmen til at vise citater. Kort sagt skal justeringen af widgetten til navneskiltet ændres fra midten til toppen. Og ville det ikke være rart at animere denne justering sammen med den tidligere cross-fade-animation? Lad os gøre det.
Som altid kan der bruges flere teknikker til at opnå dette. Da widgeten med navneskiltet allerede er centerjusteret, ville det være meget enklere at animere dens justering end at manipulere widgets værdier for top og venstre. Widgeten AnimatedAlign
er perfekt til denne opgave.
For at igangsætte denne animation er der brug for en trigger. Det eneste formål med denne widget er at animere ændring af justeringen, så den har kun få egenskaber: Tilføj et barn, indstil dets justering, udløs ændringen af justeringen, og det er det hele.
_getAnimatedAlignWidget() { return AnimatedAlign(duration: Duration(seconds: 1),curve: Curves.easeInOut,alignment: alignTop ? Alignment.topLeft : Alignment.center,child: _getAnimatedCrossfade(), );}
![](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/47d752ba-4ef2-4c69-9d7a-b802948cc22a/animated-align-400w.gif)
Den har kun to obligatoriske egenskaber:
- child:
Det barn, hvis justering vil blive ændret. - alignment:
Nødvendig justeringsværdi.
Denne widget er virkelig enkel, men resultaterne er elegante. Desuden så vi, hvor nemt vi kan bruge to forskellige animerede widgets til at skabe en mere kompleks animation. Det er det smukke ved animerede widgets.
AnimatedPadding
Nu har vi brugerens navn øverst, der er flydende animeret uden større anstrengelser ved hjælp af forskellige typer animerede widgets. Lad os tilføje en hilsen, “Hej”, før navnet. Hvis vi tilføjer en tekstwidget med værdien “Hej,” øverst, vil den overlappe hilsen-tekstwidgetten, så den ser ud som på billedet nedenfor.
![](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/487115cd-a1fc-4e31-9bb7-3738f9487c89/name-greetings-overlap.png)
Hvad nu hvis widgeten med navnetekst havde noget padding til venstre? En forøgelse af padding til venstre vil helt sikkert virke, men vent: Kan vi øge padding med noget animation? Ja, og det er det, som AnimatedPadding
gør. For at få det hele til at se meget bedre ud, kan vi lade hilsen-tekst-widgetten fade ind og lade navnetekst-widgettens padding stige på samme tid.
_getAnimatedPaddingWidget() { return AnimatedPadding( duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn, padding: increaseLeftPadding ? EdgeInsets.only(left: 28.0) : EdgeInsets.only(left: 0), child: _getAnimatedCrossfade(), );}
Da animationen ovenfor først skal ske, når den foregående animerede tilpasning er færdig, skal vi forsinke udløsningen af denne animation. Hvis vi kort afviger fra emnet, er dette et godt tidspunkt til at tale om en populær mekanisme til at tilføje forsinkelse. Flutter tilbyder flere sådanne teknikker, men konstruktøren Future.delayed er en af de enklere, renere og mere læsevenlige tilgange. For eksempel for at udføre et stykke kode efter 1 sekund:
Future.delayed(Duration(seconds: 1), (){ sum = a + b; // This sum will be calculated after 1 second. print(sum);});
Da forsinkelsesvarigheden allerede er kendt (beregnet ud fra tidligere animationsvarigheder), kan animationen udløses efter dette 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; }); });}
![](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/1eab3737-3249-4906-bfbb-d43214014082/animated-padding-400w.gif)
![](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/1eab3737-3249-4906-bfbb-d43214014082/animated-padding-400w.gif)
Denne widget har kun to obligatoriske egenskaber:
-
child
Det barn inden for denne widget, som padding vil blive anvendt på. -
padding
Mængden af plads, der skal tilføjes.
AnimatedSize
I dag vil enhver app, der har en eller anden form for animation, omfatte zoomning ind eller ud af visuelle komponenter for at fange brugerens opmærksomhed (almindeligvis kaldet skaleringsanimation). Hvorfor ikke bruge den samme teknik her? Vi kan vise brugeren et motiverende citat, som zoomes ind fra midten af skærmen. Lad mig præsentere dig for AnimatedSize
-widget’en, som aktiverer zoom ind- og zoom ud-effekter, der styres ved at ændre størrelsen på dens underordnede element.
Denne widget er lidt anderledes end de andre, når det gælder de nødvendige parametre. Vi har brug for det, som Flutter kalder en “Ticker”. Flutter har en metode til at lade objekter vide, når der udløses en ny rammehændelse. Man kan tænke på det som noget, der sender et signal, der siger: “Gør det nu! … Gør det nu! … Gør det nu! …”
AnimatedSize
Widgeten AnimatedSize
kræver en egenskab – vsync – som accepterer en ticker-provider. Den nemmeste måde at få en ticker-provider på er at tilføje en Mixin til klassen. Der er to grundlæggende ticker provider-implementeringer: SingleTickerProviderStateMixin
, som giver en enkelt ticker, og TickerProviderStateMixin
, som giver flere.
Den standardimplementering af en ticker bruges til at markere rammerne i en animation. I dette tilfælde er sidstnævnte anvendt. Mere om 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; }); });}
![](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/adda1c12-f5e1-4968-bc2f-b119f4a79c2d/animated-size-400w.gif)
Obligatoriske egenskaber for denne widget:
-
vsync
Den nødvendige ticker-provider til koordinering af animation og rammeændringer.< -
child
Det barn, hvis størrelsesændringer vil blive animeret.
Det er nu nemt at styre animationen til at zoome ind og zoome ud.
AnimatedPositioned
Glimrende! Citaterne zoomer ind fra midten for at fange brugerens opmærksomhed. Hvad nu, hvis det gled op fra bunden, mens der zoomes ind? Lad os prøve det. Denne bevægelse indebærer, at vi leger med citatwidgets position og animerer ændringerne i positionsegenskaberne. AnimatedPositioned
er den perfekte kandidat.
Denne widget overgår automatisk barnets position over en given varighed, hver gang den angivne position ændres. Et punkt at bemærke: Den fungerer kun, hvis dens overordnet widget er en “Stack”. Denne widget er ret enkel og ligetil at bruge. Lad os se.
// 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, );}
![](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/727fddfe-79bd-4812-aaed-28d39fee5993/animated-positioned-400w.gif)
Denne widget har kun én obligatorisk egenskab:
-
child
Den widget, hvis position vil blive ændret.
Hvis størrelsen af barnet ikke forventes at ændre sig sammen med dets position, ville et mere performativt alternativ til denne widget være SlideTransition
.
Her er vores komplette animation:
![](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/d74c1abe-0123-4f0f-a597-4d4b5ff3d6a6/full-video-flutter-animation-400w.gif)
Konklusion
Animationer er en integreret del af brugeroplevelsen. Statiske apps eller apps med skramlede animationer sænker ikke kun brugerfastholdelsen, men også udviklerens ry for at levere resultater.
I dag har de fleste populære apps en eller anden form for subtil animation for at glæde brugerne. Animeret feedback på brugernes anmodninger kan også få dem til at udforske mere. Flutter tilbyder mange funktioner til udvikling på tværs af platforme, herunder rig understøttelse af glidende og responsive animationer.
Flutter har stor plug-in-understøttelse, som gør det muligt for os at bruge animationer fra andre udviklere. Nu hvor det er modnet til version 1.9, med så meget kærlighed fra fællesskabet, vil Flutter helt sikkert blive bedre i fremtiden. Jeg vil sige, at nu er et godt tidspunkt at lære Flutter!
Leave a Reply