Animatie-apps met Flutter

Shubham

Over de auteur

Overdag full stack softwareontwikkelaar en ’s nachts ontwikkelaar van mobiele apps. Als ik niet werk, kook ik graag en speel ik graag PC-games.Meer overShubham↬

  • 14 min lezen
  • Apps,Animatie
  • Opgeslagen voor offline lezen
  • Delen op Twitter, LinkedIn
Flutter biedt geweldige animatie-ondersteuning voor cross-platform apps. Dit artikel gaat in op de nieuwe onconventionele en eenvoudigere manier om animaties aan apps toe te voegen. Wat zijn deze nieuwe “Animation and Motion”-widgets precies en hoe kunnen we ze gebruiken om eenvoudige en complexe animaties toe te voegen?

Apps voor welk platform dan ook worden geprezen als ze intuïtief zijn, er goed uitzien en prettige feedback geven bij gebruikersinteracties. Animatie is een van de manieren om precies dat te doen.

Flutter, een cross-platform framework, is de afgelopen twee jaar volwassen geworden en heeft nu ook web- en desktopondersteuning. Het heeft een reputatie opgebouwd dat apps die ermee zijn ontwikkeld er vloeiend en goed uitzien. Met zijn rijke animatie-ondersteuning, declaratieve manier van het schrijven van UI, “Hot Reload,” en andere functies, is het nu een compleet cross-platform framework.

Als je begint met Flutter en een onconventionele manier wilt leren om animatie toe te voegen, dan ben je op de juiste plaats: we zullen het rijk van animatie en motion widgets verkennen, een impliciete manier om animaties toe te voegen.

Flutter is gebaseerd op het concept van widgets. Elke visuele component van een app is een widget – denk aan hen als views in Android. Flutter biedt animatie ondersteuning met behulp van een Animation klasse, een “AnimationController” object voor het beheer, en “Tween” om het bereik van de gegevens te interpoleren. Deze drie componenten werken samen om vloeiende animatie te bieden. Omdat dit handmatige creatie en beheer van animatie vereist, staat het bekend als een expliciete manier van animeren.

Nu wil ik je voorstellen aan animatie en beweging widgets. Flutter biedt tal van widgets die inherent animatie ondersteunen. Het is niet nodig om een animatie object of een controller te maken, omdat alle animatie wordt afgehandeld door deze categorie van widgets. Kies gewoon de juiste widget voor de vereiste animatie en geef de waarden van de eigenschappen van de widget door om te animeren. Deze techniek is een impliciete manier van animeren.

Animatiehiërarchie in Flutter. (Groot voorbeeld)

De bovenstaande grafiek geeft ruwweg de animatiehiërarchie in Flutter weer, hoe zowel expliciete als impliciete animatie worden ondersteund.

Enkele van de geanimeerde widgets die in dit artikel worden behandeld zijn:

  • AnimatedOpacity
  • AnimatedCrossFade
  • AnimatedAlign
  • AnimatedPadding
  • AnimatedSize
  • AnimatedPositioned.

Flutter biedt niet alleen voorgedefinieerde geanimeerde widgets, maar ook een generieke widget genaamd AnimatedWidget, die kan worden gebruikt om eigen impliciet geanimeerde widgets te maken. Zoals uit de naam blijkt, behoren deze widgets tot de categorie geanimeerde en bewegende widgets, en dus hebben ze enkele gemeenschappelijke eigenschappen die ons in staat stellen animaties veel vloeiender te maken en er beter uit te laten zien.

Laat me deze gemeenschappelijke eigenschappen nu uitleggen, omdat ze later in alle voorbeelden zullen worden gebruikt.

  • duration
    De duur waarover de parameters moeten worden geanimeerd.
  • reverseDuration
    De duur van de omgekeerde animatie.
  • curve
    De curve die moet worden toegepast bij het animeren van de parameters. De geïnterpoleerde waarden kunnen worden genomen van een lineaire distributie of, indien en wanneer gespecificeerd, kunnen worden genomen van een curve.

Laten we de reis beginnen met het maken van een eenvoudige app die we “Quoted” zullen noemen. Het zal elke keer dat de app start een willekeurig citaat tonen. Twee dingen om op te merken: ten eerste, al deze citaten zullen hard gecodeerd worden in de applicatie; en ten tweede, er zullen geen gebruikersgegevens worden opgeslagen.

Note: Alle bestanden voor deze voorbeelden zijn te vinden op GitHub.

Getting Started

Flutter zou geïnstalleerd moeten zijn en u zult enige bekendheid met de basisflow moeten hebben voordat u verder gaat. Een goede plek om te beginnen is “Using Google’s Flutter For Truly Cross-Platform Mobile Development”.

Maak een nieuw Flutter-project in Android Studio.

Nieuw Flutter-projectmenu in Android Studio. (Groot voorbeeld)

Dit opent een nieuwe projectwizard, waar u de basisprincipes van het project kunt configureren.

Selectiescherm voor het type Flutter-project. (Groot voorbeeld)

In het selectiescherm voor projecttypen zijn er verschillende typen Flutter-projecten, die elk zijn afgestemd op een specifiek scenario. Voor deze tutorial kiest u Flutter Application en drukt u op Next.

U moet nu enkele projectspecifieke gegevens invoeren: de projectnaam en het projectpad, het bedrijfsdomein, enzovoort. Bekijk de onderstaande afbeelding.

Flutter-toepassing configuratiescherm. (Groot voorbeeld)

Voeg de projectnaam, het Flutter SDK-pad, de projectlocatie en een optionele projectbeschrijving in. Druk op Volgende.

Het scherm met de naam van het Flutter-toepassingspakket. (Groot voorbeeld)

Elke toepassing (of het nu Android of iOS is) vereist een unieke pakketnaam. Meestal gebruikt u het omgekeerde van uw websitedomein; bijvoorbeeld com.google of com.yahoo. Druk op Voltooien om een werkende Flutter-toepassing te genereren.

Het gegenereerde voorbeeldproject. (Groot voorbeeld)

Als het project is gegenereerd, zou u het hierboven getoonde scherm moeten zien. Open het bestand main.dart (gemarkeerd in de schermafbeelding). Dit is het hoofdbestand van de toepassing. Het voorbeeldproject is op zichzelf compleet, en kan direct op een emulator of een fysiek apparaat worden uitgevoerd zonder enige wijziging.

Vervang de inhoud van het bestand main.dart door het volgende codefragment:

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(), ); }}

Deze code schoont het bestand main.dart op door alleen eenvoudige informatie toe te voegen die relevant is voor het maken van een nieuwe app. De klasse MyApp retourneert een object: een MaterialApp-widget, die de basisstructuur biedt voor het maken van apps die voldoen aan Material Design. Om de code meer gestructureerd te maken, maak je twee nieuwe dart-bestanden in de lib-map: FirstPage.dart en Quotes.dart.

Het bestand FirstPage.dart. (Groot voorbeeld)

FirstPage.dart zal alle code bevatten die verantwoordelijk is voor alle visuele elementen (widgets) die nodig zijn voor onze Citaten-app. Alle animatie wordt in dit bestand afgehandeld.

Note: Later in het artikel worden alle codefragmenten voor elke geanimeerde widget aan dit bestand toegevoegd als kinderen van de Scaffold widget. Voor meer informatie kan dit voorbeeld op GitHub nuttig zijn.

Start met het toevoegen van de volgende code aan FirstPage.dart. Dit is de gedeeltelijke code waar later andere dingen aan zullen worden toegevoegd.

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: , ), ); }}
Het bestand Quotes.dart. (Groot voorbeeld)

Het bestand Quotes.dart bevat een lijst met alle hardcoded citaten. Een punt om hier op te merken is dat de lijst een statisch object is. Dit betekent dat het op andere plaatsen kan worden gebruikt zonder een nieuw object van de klasse Citaten aan te maken. Dit is zo ontworpen, omdat de bovenstaande lijst simpelweg als een hulpprogramma fungeert.

Voeg de volgende code toe aan dit bestand:

class Quotes { static const quotesArray = ;}

Het projectskelet is nu klaar, dus laten we Quoted nog wat verder uitwerken.

AnimatedOpacity

Om de app een persoonlijk tintje te geven, zou het leuk zijn om de naam van de gebruiker te weten, dus laten we daar om vragen en een volgende knop tonen. Totdat de gebruiker zijn naam invult, is deze knop verborgen, en hij zal verschijnen als een naam is gegeven. We hebben een soort zichtbaarheids-animatie nodig voor de knop, maar is daar een widget voor? Ja, die is er.

Enter AnimatedOpacity. Deze widget bouwt voort op de Opacity widget door impliciete animatie-ondersteuning toe te voegen. Hoe gebruiken we het? Denk aan ons scenario: we moeten een volgende knop tonen met geanimeerde zichtbaarheid. We wikkelen de knop-widget binnen de AnimatedOpacity-widget, voeren enkele juiste waarden in en voegen een voorwaarde toe om de animatie te triggeren – en Flutter kan de rest afhandelen.

_getAnimatedOpacityButton() { return AnimatedOpacity( duration: Duration(seconds: 1), reverseDuration: Duration(seconds: 1), curve: Curves.easeInOut, opacity: showNextButton ? 1 : 0, child: _getButton(), );}
Opacity animation of next button. (Groot voorbeeld)

De AnimatedOpacity widget heeft twee verplichte eigenschappen:

  • opacity
    Een waarde van 1 betekent volledig zichtbaar; 0 (nul) betekent verborgen. Tijdens het animeren interpoleert Flutter waarden tussen deze twee uitersten. U kunt zien hoe een voorwaarde wordt geplaatst om de zichtbaarheid te veranderen en zo de animatie te triggeren.
  • child
    Het kind-widget waarvan de zichtbaarheid wordt geanimeerd.

U zou nu moeten begrijpen hoe heel eenvoudig het is om zichtbaarheid-animatie toe te voegen met de impliciete widget. En al deze widgets volgen dezelfde richtlijnen en zijn eenvoudig te gebruiken. Laten we naar de volgende gaan.

AnimatedCrossFade

We hebben de naam van de gebruiker, maar de widget wacht nog steeds op invoer. In de vorige stap, als de gebruiker zijn naam invoert, tonen we de volgende knop. Nu, wanneer de gebruiker op de knop drukt, wil ik stoppen met het accepteren van invoer en de ingevoerde naam tonen. Er zijn natuurlijk vele manieren om dit te doen, maar misschien kunnen we de invoer-widget verbergen en een niet te wijzigen tekst-widget tonen. Laten we het uitproberen met de AnimatedCrossFade widget.

Deze widget heeft twee kinderen nodig, omdat de widget tussen hen heen en weer schakelt op basis van een of andere voorwaarde. Een belangrijk ding om in gedachten te houden bij het gebruik van deze widget is dat beide kinderen dezelfde breedte moeten hebben. Als de hoogte verschilt, dan wordt de langere widget van onderen geclipt. In dit scenario worden twee widgets als children gebruikt: input en 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, );}
Cross-fading tussen de input widget en de name widget. (Groot voorbeeld)

Deze widget vereist een andere set verplichte parameters:

  • crossFadeState
    Deze staat bepaalt welk kind moet worden getoond.
  • firstChild
    Specificeert het eerste kind voor deze widget.
  • secondChild
    Specificeert het tweede kind.

AnimatedAlign

Op dit punt is het naamlabel in het midden van het scherm gepositioneerd. Het ziet er bovenaan veel beter uit, omdat we het midden van het scherm nodig hebben om aanhalingstekens te tonen. Simpel gezegd, de uitlijning van de naamlabel widget moet worden veranderd van midden naar boven. En zou het niet leuk zijn om deze uitlijning te animeren samen met de vorige cross-fade animatie? Laten we het doen.

Zoals altijd, kunnen verschillende technieken worden gebruikt om dit te bereiken. Omdat de naamlabel widget al gecentreerd is uitgelijnd, zou het animeren van de uitlijning veel eenvoudiger zijn dan het manipuleren van de boven- en linkerwaarden van de widget. De AnimatedAlign widget is perfect voor deze taak.

Om deze animatie te starten, is een trigger nodig. Het enige doel van deze widget is de uitlijningsverandering te animeren, dus het heeft maar een paar eigenschappen: een kind toevoegen, de uitlijning instellen, de uitlijningsverandering triggeren, en dat is het.

_getAnimatedAlignWidget() { return AnimatedAlign(duration: Duration(seconds: 1),curve: Curves.easeInOut,alignment: alignTop ? Alignment.topLeft : Alignment.center,child: _getAnimatedCrossfade(), );}
Uitlijningsanimatie van de naam-widget. (Groot voorbeeld)

Het heeft slechts twee verplichte eigenschappen:

  • child:
    Het kind waarvan de uitlijning zal worden gewijzigd.
  • uitlijning:
    Verplichte uitlijningswaarde.

Deze widget is echt eenvoudig, maar de resultaten zijn elegant. Bovendien hebben we gezien hoe gemakkelijk we twee verschillende geanimeerde widgets kunnen gebruiken om een complexere animatie te maken. Dit is de schoonheid van geanimeerde widgets.

AnimatedPadding

Nu hebben we de naam van de gebruiker bovenaan, vloeiend geanimeerd zonder veel moeite, door gebruik te maken van verschillende soorten geanimeerde widgets. Laten we een begroeting toevoegen, “Hi,” voor de naam. Door een tekstwidget met de waarde “Hi,” bovenaan toe te voegen, overlapt deze de begroetingstekstwidget, zoals in de afbeelding hieronder.

De begroeting- en naamwidgets overlappen elkaar. (Groot voorbeeld)

Wat als de naamwidget aan de linkerkant wat opvulling zou hebben? Het vergroten van de linker padding zal zeker werken, maar wacht: kunnen we de padding vergroten met wat animatie? Ja, en dat is wat AnimatedPadding doet. Om dit alles er veel beter uit te laten zien, laten we de tekstwidget met de begroetingen vervagen en de padding van de naamwidget tegelijkertijd toenemen.

_getAnimatedPaddingWidget() { return AnimatedPadding( duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn, padding: increaseLeftPadding ? EdgeInsets.only(left: 28.0) : EdgeInsets.only(left: 0), child: _getAnimatedCrossfade(), );}

Omdat de animatie hierboven pas moet plaatsvinden nadat de vorige uitlijning met animatie is voltooid, moeten we het starten van deze animatie uitstellen. Nu we even van het onderwerp afwijken, is dit een goed moment om het te hebben over een populair mechanisme om vertraging toe te voegen. Flutter biedt verschillende van dergelijke technieken, maar de constructor Future.delayed is een van de eenvoudigere, schonere en beter leesbare benaderingen. Bijvoorbeeld, om een stukje code na 1 seconde uit te voeren:

Future.delayed(Duration(seconds: 1), (){ sum = a + b; // This sum will be calculated after 1 second. print(sum);});

Omdat de vertragingsduur al bekend is (berekend op basis van eerdere animatieduur), kan de animatie na dit interval worden getriggerd.

// 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; }); });}
Vertragingsanimatie van de naam widget. (Groot voorbeeld)

Deze widget heeft slechts twee verplichte eigenschappen:

  • child
    Het kind binnen deze widget, waarop de opvulling zal worden toegepast.
  • padding
    De hoeveelheid ruimte die moet worden toegevoegd.

AnimatedSize

In elke app met een animatie wordt tegenwoordig ingezoomd op of uitgezoomd uit visuele componenten om de aandacht van de gebruiker te trekken (ook wel scaling animation genoemd). Waarom zouden we hier niet dezelfde techniek gebruiken? We kunnen de gebruiker een motiverende quote laten zien die inzoomt vanuit het midden van het scherm. Laat me je voorstellen aan de AnimatedSize widget, die de zoom-in en zoom-uit effecten mogelijk maakt, gecontroleerd door het veranderen van de grootte van zijn child.

Deze widget is een beetje anders dan de anderen als het gaat om de vereiste parameters. We hebben nodig wat Flutter een “Ticker” noemt. Flutter heeft een methode om objecten te laten weten wanneer een nieuw frame event wordt getriggerd. Je kunt het zien als iets dat een signaal uitzendt dat zegt: “Doe het nu! … Doe het nu! … Doe het nu! …”

De AnimatedSize widget vereist een eigenschap – vsync – die een ticker provider accepteert. De eenvoudigste manier om een ticker provider te krijgen is door een Mixin aan de klasse toe te voegen. Er zijn twee basis ticker provider implementaties: SingleTickerProviderStateMixin, die een enkele ticker levert; en TickerProviderStateMixin, die er meerdere levert.

De standaard implementatie van een Ticker wordt gebruikt om de frames van een animatie te markeren. In dit geval wordt de laatste gebruikt. Meer over 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; }); });}
Omschaling animatie van de citaten widget. (Groot voorbeeld)

Verplichte eigenschappen voor deze widget:

  • vsync
    De vereiste ticker provider om animatie en frame veranderingen te coördineren.<
  • child
    Het kind waarvan de grootte verandert zal worden geanimeerd.

De in- en uitzoom animatie is nu makkelijk te temmen.

AnimatedPositioned

Geweldig! De aanhalingstekens zoomen in vanuit het midden om de aandacht van de gebruiker te trekken. Wat als het van onder naar boven schuift terwijl je inzoomt? Laten we het eens proberen. Deze beweging houdt in dat we spelen met de positie van de quote widget en de veranderingen in de positie-eigenschappen animeren. AnimatedPositioned is de perfecte kandidaat.

Deze widget verplaatst automatisch de positie van het kind over een bepaalde duur wanneer de opgegeven positie verandert. Een punt om op te merken: het werkt alleen als de ouder-widget een “Stack” is. Deze widget is vrij simpel en eenvoudig te gebruiken. Laten we eens kijken.

// 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, );}
Positie met schalingsanimatie van aanhalingstekens. (Groot voorbeeld)

Deze widget heeft slechts één verplichte eigenschap:

  • child
    De widget waarvan de positie zal worden gewijzigd.

Als niet verwacht wordt dat de grootte van het kind samen met de positie verandert, zou een performanter alternatief voor deze widget SlideTransition zijn.

Hier is onze complete animatie:

Alle geanimeerde widgets samen. (Grote preview)

Conclusie

Animaties zijn een integraal onderdeel van de gebruikerservaring. Statische apps of apps met een rommelige animatie verlagen niet alleen de retentie van gebruikers, maar ook de reputatie van een ontwikkelaar om resultaten te leveren.

De meeste populaire apps hebben tegenwoordig een vorm van subtiele animatie om gebruikers te verrukken. Geanimeerde feedback op verzoeken van gebruikers kan hen ook engageren om meer te verkennen. Flutter biedt veel functies voor cross-platform ontwikkeling, waaronder rijke ondersteuning voor vloeiende en responsieve animaties.

Flutter heeft geweldige plug-in ondersteuning die ons in staat stelt om animaties van andere ontwikkelaars te gebruiken. Nu dat het is gerijpt tot versie 1.9, met zoveel liefde van de gemeenschap, is Flutter gebonden om beter te worden in de toekomst. Ik zou zeggen dat het nu een geweldige tijd is om Flutter te leren!

Leave a Reply