Animating Apps With Flutter

Shubham

About The Author

A full stack software developer by the day and mobile app developer by night. Kiedy nie pracuje, uwielbiam gotować i grać w gry komputerowe.More aboutShubham↬

  • 14 min read
  • Apps,Animation
  • Saved for offline reading
  • Share on Twitter, LinkedIn
Flutter zapewnia świetne wsparcie animacji dla aplikacji międzyplatformowych. Ten artykuł odkrywa nowy, niekonwencjonalny i łatwiejszy sposób dodawania animacji do aplikacji. Czym dokładnie są te nowe widżety „Animation and Motion” i jak możemy ich użyć, aby dodać proste i złożone animacje?

Aplikacje na każdą platformę są chwalone, gdy są intuicyjne, dobrze wyglądające i dostarczają przyjemnych informacji zwrotnych do interakcji użytkownika. Animacja jest jednym ze sposobów, aby to właśnie osiągnąć.

Flutter, wieloplatformowy framework, dojrzał w ciągu ostatnich dwóch lat do obsługi stron internetowych i komputerów stacjonarnych. Zdobył reputację, że aplikacje stworzone przy jego użyciu są gładkie i dobrze wyglądające. Dzięki bogatemu wsparciu dla animacji, deklaratywnemu sposobowi pisania UI, „Hot Reload” i innym funkcjom, jest to obecnie kompletny framework wieloplatformowy.

Jeśli zaczynasz pracę z Flutterem i chcesz poznać niekonwencjonalny sposób dodawania animacji, to jesteś we właściwym miejscu: zbadamy sferę animacji i widżetów ruchu, niejawny sposób dodawania animacji.

Flutter jest oparty na koncepcji widżetów. Każdy wizualny komponent aplikacji jest widżetem – pomyśl o nich jak o widokach w Androidzie. Flutter zapewnia obsługę animacji za pomocą klasy Animation, obiektu „AnimationController” do zarządzania, oraz „Tween” do interpolacji zakresu danych. Te trzy komponenty współpracują ze sobą, aby zapewnić płynną animację. Ponieważ wymaga to ręcznego tworzenia i zarządzania animacją, jest to znane jako jawny sposób animowania.

Teraz pozwól, że przedstawię Ci widżety animacji i ruchu. Flutter dostarcza wiele widżetów, które z natury wspierają animację. Nie ma potrzeby tworzenia obiektu animacji ani żadnego kontrolera, ponieważ cała animacja jest obsługiwana przez tę kategorię widżetów. Wystarczy wybrać odpowiedni widżet dla wymaganej animacji i przekazać wartości właściwości widżetu do animacji. Ta technika jest niejawnym sposobem animowania.

Hierarchia animacji we Flutterze. (Duży podgląd)

Powyższy wykres z grubsza określa hierarchię animacji we Flutterze, jak obsługiwane są zarówno jawne jak i niejawne animacje.

Niektóre z animowanych widżetów omówionych w tym artykule to:

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

Flutter nie tylko dostarcza predefiniowane animowane widżety, ale także widżet generyczny o nazwie AnimatedWidget, który może być użyty do tworzenia niestandardowych, niejawnie animowanych widżetów. Jak wynika z nazwy, widżety te należą do kategorii widżetów animacji i ruchu, a więc mają pewne wspólne właściwości, które pozwalają nam tworzyć animacje znacznie płynniejsze i lepiej wyglądające.

Pozwól mi teraz wyjaśnić te wspólne właściwości, ponieważ będą one używane później we wszystkich przykładach.

  • duration
    Czas trwania, w którym należy animować parametry.
  • reverseDuration
    Czas trwania animacji odwrotnej.
  • curve
    Krzywa, którą należy zastosować podczas animowania parametrów. Interpolowane wartości mogą być pobierane z rozkładu liniowego lub, jeśli i kiedy są określone, mogą być pobierane z krzywej.

Zacznijmy podróż od stworzenia prostej aplikacji, którą nazwiemy „Quoted”. Będzie ona wyświetlać losowy cytat za każdym razem, gdy aplikacja się uruchomi. Dwie rzeczy do odnotowania: po pierwsze, wszystkie te cytaty będą hardkodowane w aplikacji; a po drugie, żadne dane użytkownika nie będą zapisywane.

Uwaga: Wszystkie pliki dla tych przykładów można znaleźć na GitHub.

Getting Started

Flutter powinien być zainstalowany i będziesz potrzebował trochę znajomości z podstawowym przepływem przed przejściem dalej. Dobrym miejscem do rozpoczęcia jest „Using Google’s Flutter For Truly Cross-Platform Mobile Development”.

Utwórz nowy projekt Flutter w Android Studio.

Menu nowego projektu Flutter w Android Studio. (Duży podgląd)

To otworzy kreator nowego projektu, w którym można skonfigurować podstawy projektu.

Ekran wyboru typu projektu Flutter. (Duży podgląd)

Na ekranie wyboru typu projektu znajdują się różne typy projektów Flutter, z których każdy jest dostosowany do konkretnego scenariusza… Dla tego samouczka wybierz Flutter Application i naciśnij Next.

Teraz musisz wprowadzić kilka informacji specyficznych dla projektu: nazwę i ścieżkę projektu, domenę firmy i tak dalej. Spójrz na poniższy obrazek.

Ekran konfiguracji aplikacji Flutter. (Duży podgląd)

Dodaj nazwę projektu, ścieżkę Flutter SDK, lokalizację projektu i opcjonalny opis projektu. Naciśnij Dalej.

Ekran nazwy pakietu aplikacji Flutter. (Duży podgląd)

Każda aplikacja (czy to Android, czy iOS) wymaga unikalnej nazwy pakietu. Zazwyczaj używasz odwrotności domeny swojej strony internetowej; na przykład com.google lub com.yahoo. Naciśnij Zakończ, aby wygenerować działającą aplikację Flutter.

Generowany przykładowy projekt. (Duży podgląd)

Po wygenerowaniu projektu, powinieneś zobaczyć ekran pokazany powyżej. Otwórz plik main.dart (zaznaczony na zrzucie ekranu). Jest to główny plik aplikacji. Przykładowy projekt jest kompletny sam w sobie i może być uruchomiony bezpośrednio na emulatorze lub fizycznym urządzeniu bez żadnych modyfikacji.

Zastąp zawartość pliku main.dart następującym wycinkiem kodu:

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

Ten kod czyści plik main.dart, dodając jedynie proste informacje istotne przy tworzeniu nowej aplikacji. Klasa MyApp zwraca obiekt: widżet MaterialApp, który zapewnia podstawową strukturę do tworzenia aplikacji zgodnych z Material Design. Aby kod był bardziej uporządkowany, utwórz dwa nowe pliki dart wewnątrz folderu lib: FirstPage.dart i Quotes.dart.

Plik FirstPage.dart. (Duży podgląd)

FirstPage.dart będzie zawierał cały kod odpowiedzialny za wszystkie elementy wizualne (widżety) wymagane w naszej aplikacji Quoted. Wszystkie animacje są obsługiwane w tym pliku.

Uwaga: W dalszej części artykułu, wszystkie fragmenty kodu dla każdego animowanego widgetu są dodawane do tego pliku jako dzieci widgetu Scaffold. Aby uzyskać więcej informacji, ten przykład na GitHub może być przydatny.

Zacznij od dodania następującego kodu do FirstPage.dart. To jest częściowy kod, gdzie inne rzeczy zostaną dodane później.

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

Plik Quotes.dart. (Duży podgląd)

Plik Quotes.dart zawiera listę wszystkich zakodowanych cytatów. Należy zwrócić uwagę na to, że lista jest obiektem statycznym. Oznacza to, że może być użyta w innych miejscach bez konieczności tworzenia nowego obiektu klasy Quotes. Zostało to wybrane przez projekt, ponieważ powyższa lista działa po prostu jako narzędzie.

Dodaj następujący kod do tego pliku:

class Quotes { static const quotesArray = ;}

Szkielet projektu jest już gotowy, więc wypełnijmy Quoted trochę bardziej.

AnimatedOpacity

Aby nadać aplikacji osobisty charakter, dobrze byłoby znać imię użytkownika, więc zapytajmy o nie i pokażmy przycisk next. Dopóki użytkownik nie poda swojego imienia, przycisk ten jest ukryty, a pojawi się, gdy imię zostanie podane. Potrzebujemy jakiejś animacji widoczności dla przycisku, ale czy istnieje widżet do tego celu? Tak, jest.

Enter AnimatedOpacity. Ten widżet opiera się na widżecie Opacity, dodając obsługę animacji ukrytej. Jak z niego korzystać? Przypomnij sobie nasz scenariusz: musimy wyświetlić przycisk next z animowaną widocznością. Zawijamy widżet przycisku wewnątrz widżetu AnimatedOpacity, podajemy odpowiednie wartości i dodajemy warunek, aby wywołać animację – a Flutter zajmie się resztą.

_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. (Duży podgląd)

Widżet AnimatedOpacity ma dwie obowiązkowe właściwości:

  • opacity
    Wartość 1 oznacza, że jest całkowicie widoczny; 0 (zero) oznacza, że jest ukryty. Podczas animacji, Flutter interpoluje wartości pomiędzy tymi dwoma skrajnościami. Możesz zobaczyć, jak warunek jest umieszczony, aby zmienić widoczność, tym samym uruchamiając animację.
  • child
    Widżet dziecka, który będzie miał animowaną widoczność.

Powinieneś teraz zrozumieć, jak naprawdę proste jest dodanie animacji widoczności za pomocą widżetu implicite. A wszystkie takie widżety podążają za tymi samymi wytycznymi i są łatwe w użyciu. Przejdźmy do kolejnego z nich.

AnimatedCrossFade

Mamy już nazwę użytkownika, ale widżet nadal czeka na dane wejściowe. W poprzednim kroku, gdy użytkownik wprowadzi swoje imię, wyświetlimy przycisk next. Teraz, gdy użytkownik naciśnie przycisk, chcę zatrzymać przyjmowanie danych wejściowych i pokazać wpisane imię. Można to oczywiście zrobić na wiele sposobów, ale być może uda nam się ukryć widżet wprowadzania danych i wyświetlić nieedytowalny widżet tekstowy. Wypróbujmy to za pomocą widżetu AnimatedCrossFade.

Ten widżet wymaga dwóch dzieci, ponieważ widżet przełącza się między nimi na podstawie pewnego warunku. Jedną z ważnych rzeczy, o których należy pamiętać podczas korzystania z tego widżetu, jest to, że oba dzieci powinny mieć taką samą szerokość. Jeśli wysokość jest inna, wtedy wyższy widżet zostanie przycięty od dołu. W tym scenariuszu, dwa widżety będą używane jako dzieci: 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, );}
Cross-fading pomiędzy widżetem input i widżetem name. (Duży podgląd)

Ten widget wymaga innego zestawu parametrów obowiązkowych:

  • crossFadeState
    Ten stan sprawdza, które dziecko pokazać.
  • firstChild
    Wyznacza pierwsze dziecko dla tego widgetu.
  • secondChild
    Wyznacza drugie dziecko.

AnimatedAlign

W tym momencie etykieta z nazwą jest umieszczona na środku ekranu. Będzie ona wyglądać znacznie lepiej na górze, ponieważ potrzebujemy środka ekranu do wyświetlania cytatów. Mówiąc prościej, wyrównanie widżetu etykiety nazwy powinno zostać zmienione z centralnego na górne. I czy nie byłoby miło animować tę zmianę wyrównania wraz z poprzednią animacją cross-fade? Zróbmy to.

Jak zawsze, można użyć kilku technik, aby to osiągnąć. Ponieważ widżet etykiety z nazwą jest już wyrównany do środka, animowanie jego wyrównania byłoby znacznie prostsze niż manipulowanie wartościami top i left widżetu. Widżet AnimatedAlign jest idealny do tego zadania.

Aby zainicjować tę animację, potrzebny jest wyzwalacz. Jedynym celem tego widżetu jest animacja zmiany wyrównania, więc ma on tylko kilka właściwości: dodaj dziecko, ustaw jego wyrównanie, wywołaj zmianę wyrównania i to wszystko.

_getAnimatedAlignWidget() { return AnimatedAlign(duration: Duration(seconds: 1),curve: Curves.easeInOut,alignment: alignTop ? Alignment.topLeft : Alignment.center,child: _getAnimatedCrossfade(), );}
Animacja wyrównania widżetu nazwy. (Duży podgląd)

Ma tylko dwie obowiązkowe właściwości:

  • child:
    Dziecko, którego wyrównanie zostanie zmodyfikowane.
  • alignment:
    Wymagana wartość wyrównania.

Ten widżet jest naprawdę prosty, ale rezultaty są eleganckie. Co więcej, zobaczyliśmy jak łatwo możemy użyć dwóch różnych animowanych widżetów do stworzenia bardziej złożonej animacji. To jest właśnie piękno animowanych widżetów.

AnimatedPadding

Teraz mamy nazwę użytkownika na górze, płynnie animowaną bez większego wysiłku, przy użyciu różnych rodzajów animowanych widżetów. Dodajmy jeszcze pozdrowienie „Cześć” przed imieniem. Dodanie widżetu tekstowego o wartości „Hi,” na górze spowoduje, że będzie on zachodził na widżet tekstowy powitania, wyglądając jak na poniższym obrazku.

Widżety powitania i imienia zachodzą na siebie. (Duży podgląd)

Co by było, gdyby tekst powitania miał trochę wypełnienia po lewej stronie? Zwiększenie lewego paddingu na pewno zadziała, ale zaraz: czy możemy zwiększyć padding za pomocą jakiejś animacji? Tak, i to właśnie robi AnimatedPadding. Aby to wszystko wyglądało o wiele lepiej, sprawmy, aby tekst powitania zanikał, a wypełnienia widżetu tekstowego z nazwą zwiększały się w tym samym czasie.

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

Ponieważ powyższa animacja powinna wystąpić dopiero po zakończeniu poprzedniego animowanego wyrównania, musimy opóźnić wywołanie tej animacji. Odbiegając na chwilę od tematu, jest to dobry moment, aby opowiedzieć o popularnym mechanizmie dodawania opóźnień. Flutter udostępnia kilka takich technik, ale konstruktor Future.delayed jest jednym z prostszych, czystszych i bardziej czytelnych podejść. Przykładowo, aby wykonać fragment kodu po 1 sekundzie:

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

Ponieważ czas trwania opóźnienia jest już znany (obliczony na podstawie poprzednich czasów trwania animacji), animacja może zostać wyzwolona po tym interwale.

// 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; }); });}
Animacja widżetu nazwy. (Duży podgląd)

Ten widget ma tylko dwie obowiązkowe właściwości:

  • child
    Dziecko wewnątrz tego widgetu, do którego zostanie zastosowane padding.
  • padding
    Ilość miejsca do dodania.

AnimatedSize

Dzisiaj, każda aplikacja posiadająca jakiś rodzaj animacji będzie zawierała powiększanie lub pomniejszanie elementów wizualnych w celu przyciągnięcia uwagi użytkownika (powszechnie nazywane animacją skalowania). Dlaczego nie użyć tej samej techniki tutaj? Możemy pokazać użytkownikowi cytat motywacyjny, który powiększa się od środka ekranu. Pozwól, że przedstawię Ci widżet AnimatedSize, który umożliwia efekty powiększania i pomniejszania, kontrolowane przez zmianę rozmiaru jego dziecka.

Ten widżet różni się nieco od pozostałych, jeśli chodzi o wymagane parametry. Potrzebujemy czegoś, co Flutter nazywa „Ticker”. Flutter ma metodę, aby dać obiektom znać, gdy tylko zostanie wywołane zdarzenie nowej ramki. Można to sobie wyobrazić jako coś, co wysyła sygnał mówiący: „Zrób to teraz! … Zrób to teraz! … Zrób to teraz! …”

Widżet AnimatedSize wymaga właściwości – vsync – która akceptuje dostawcę tickera. Najprostszym sposobem na uzyskanie dostawcy tikera jest dodanie Mixina do klasy. Istnieją dwie podstawowe implementacje dostawcy tikera: SingleTickerProviderStateMixin, która zapewnia pojedynczy ticker; oraz TickerProviderStateMixin, która zapewnia kilka.

Domyślna implementacja Tickera jest używana do oznaczania klatek animacji. W tym przypadku zastosowano to drugie rozwiązanie. Więcej o mixinach.

// 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; }); });}
Skalowanie animacji widżetu cytatów. (Duży podgląd)

Właściwości obowiązkowe dla tego widgetu:

  • vsync
    Wymagany dostawca tickera do koordynowania animacji i zmian ramek.<
  • child
    Dziecko, którego zmiany rozmiaru będą animowane.

Animacja powiększania i pomniejszania jest teraz łatwa do opanowania.

AnimatedPositioned

Świetnie! Cytaty powiększają się od środka, aby przyciągnąć uwagę użytkownika. Co by było, gdyby przesuwały się w górę od dołu podczas powiększania? Spróbujmy tego. Ten ruch wymaga zabawy z pozycją widżetu cytatów i animowania zmian we właściwościach pozycji. AnimatedPositioned jest idealnym kandydatem.

Ten widżet automatycznie zmienia pozycję dziecka przez określony czas, gdy tylko zmieni się określona pozycja. Jedna uwaga: działa on tylko wtedy, gdy jego widżet nadrzędny jest „stosem”. Ten widżet jest dość prosty i łatwy w użyciu. Zobaczmy.

// 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, );}
Pozycja z animacją skalowania cytatów. (Duży podgląd)

Ten widżet ma tylko jedną obowiązkową właściwość:

  • child
    Widżet, którego pozycja będzie zmieniana.

Jeśli nie oczekuje się, że rozmiar dziecka będzie się zmieniał wraz z jego pozycją, bardziej performatywną alternatywą dla tego widżetu będzie SlideTransition.

Tutaj jest nasza kompletna animacja:

Wszystkie animowane widżety razem. (Duży podgląd)

Wniosek

Animacje są integralną częścią doświadczenia użytkownika. Statyczne aplikacje lub aplikacje z beznadziejnymi animacjami nie tylko obniżają poziom utrzymania użytkownika, ale także reputację dewelopera w zakresie dostarczania wyników.

Dzisiaj większość popularnych aplikacji ma jakiś rodzaj subtelnej animacji, aby zachwycić użytkowników. Animowane reakcje na prośby użytkowników mogą również zaangażować ich do dalszego odkrywania. Flutter oferuje wiele funkcji dla rozwoju międzyplatformowego, w tym bogate wsparcie dla płynnych i responsywnych animacji.

Flutter ma świetne wsparcie dla wtyczek, co pozwala nam korzystać z animacji od innych deweloperów. Teraz, gdy dojrzał do wersji 1.9, z tak wielką miłością ze strony społeczności, Flutter na pewno będzie jeszcze lepszy w przyszłości. Powiedziałbym, że teraz jest świetny czas na naukę Fluttera!

.

Leave a Reply