Flutter - Animazione

L'animazione è una procedura complessa in qualsiasi applicazione mobile. Nonostante la sua complessità, Animation migliora l'esperienza dell'utente a un nuovo livello e fornisce una ricca interazione dell'utente. Grazie alla sua ricchezza, l'animazione diventa parte integrante della moderna applicazione mobile. Il framework Flutter riconosce l'importanza dell'animazione e fornisce un framework semplice e intuitivo per sviluppare tutti i tipi di animazioni.

introduzione

L'animazione è un processo di visualizzazione di una serie di immagini / immagini in un ordine particolare entro una durata specifica per dare un'illusione di movimento. Gli aspetti più importanti dell'animazione sono i seguenti:

  • L'animazione ha due valori distinti: valore iniziale e valore finale. L'animazione parte dal valore iniziale e passa attraverso una serie di valori intermedi e infine termina ai valori finali. Ad esempio, per animare un widget in modo che svanisca, il valore iniziale sarà l'opacità completa e il valore finale sarà l'opacità zero.

  • I valori intermedi possono essere di natura lineare o non lineare (curva) e possono essere configurati. Comprendi che l'animazione funziona così come è configurata. Ogni configurazione fornisce una sensazione diversa all'animazione. Ad esempio, la dissolvenza di un widget sarà di natura lineare mentre il rimbalzo di una palla sarà di natura non lineare.

  • La durata del processo di animazione influisce sulla velocità (lentezza o solidità) dell'animazione.

  • La capacità di controllare il processo di animazione come l'avvio dell'animazione, l'arresto dell'animazione, la ripetizione dell'animazione per impostare il numero di volte, l'inversione del processo di animazione, ecc.,

  • In Flutter, il sistema di animazione non esegue alcuna animazione reale. Invece, fornisce solo i valori richiesti a ogni fotogramma per il rendering delle immagini.

Classi basate sull'animazione

Il sistema di animazione Flutter si basa su oggetti Animation. Le classi di animazione principali e il suo utilizzo sono i seguenti:

Animazione

Genera valori interpolati tra due numeri per una certa durata. Le classi di animazione più comuni sono:

  • Animation<double> - interpola i valori tra due numeri decimali

  • Animation<Color> - interpolare i colori tra due colori

  • Animation<Size> - interpolare le taglie tra due taglie

  • AnimationController- Oggetto Animazione speciale per controllare l'animazione stessa. Genera nuovi valori ogni volta che l'applicazione è pronta per un nuovo frame. Supporta l'animazione lineare e il valore inizia da 0,0 a 1,0

controller = AnimationController(duration: const Duration(seconds: 2), vsync: this);

Qui, il controller controlla l'animazione e l'opzione durata controlla la durata del processo di animazione. vsync è un'opzione speciale utilizzata per ottimizzare la risorsa utilizzata nell'animazione.

CurvedAnimation

Simile a AnimationController ma supporta l'animazione non lineare. CurvedAnimation può essere utilizzato insieme all'oggetto Animation come di seguito:

controller = AnimationController(duration: const Duration(seconds: 2), vsync: this); 
animation = CurvedAnimation(parent: controller, curve: Curves.easeIn)

Tween <T>

Derivato da Animatable <T> e utilizzato per generare numeri tra due numeri qualsiasi diversi da 0 e 1. Può essere utilizzato insieme all'oggetto Animation utilizzando il metodo Animate e passando l'oggetto Animation effettivo.

AnimationController controller = AnimationController( 
   duration: const Duration(milliseconds: 1000), 
vsync: this); Animation<int> customTween = IntTween(
   begin: 0, end: 255).animate(controller);
  • Tween può anche essere utilizzato insieme a CurvedAnimation come di seguito:

AnimationController controller = AnimationController(
   duration: const Duration(milliseconds: 500), vsync: this); 
final Animation curve = CurvedAnimation(parent: controller, curve: Curves.easeOut); 
Animation<int> customTween = IntTween(begin: 0, end: 255).animate(curve);

In questo caso, il controller è il controller di animazione effettivo. curve fornisce il tipo di non linearità e customTween fornisce un intervallo personalizzato da 0 a 255.

Flusso di lavoro dell'animazione Flutter

Il flusso di lavoro dell'animazione è il seguente:

  • Definisci e avvia il controller di animazione nell'initState di StatefulWidget.

AnimationController(duration: const Duration(seconds: 2), vsync: this); 
animation = Tween<double>(begin: 0, end: 300).animate(controller); 
controller.forward();
  • Aggiungi listener basato su animazione, addListener per modificare lo stato del widget.

animation = Tween<double>(begin: 0, end: 300).animate(controller) ..addListener(() {
   setState(() { 
      // The state that has changed here is the animation object’s value. 
   }); 
});
  • I widget incorporati, AnimatedWidget e AnimatedBuilder possono essere utilizzati per saltare questo processo. Entrambi i widget accettano oggetti Animazione e ottengono i valori correnti richiesti per l'animazione.

  • Ottieni i valori dell'animazione durante il processo di creazione del widget e quindi applicali per larghezza, altezza o qualsiasi proprietà rilevante invece del valore originale.

child: Container( 
   height: animation.value, 
   width: animation.value, 
   child: <Widget>, 
)

Applicazione di lavoro

Scriviamo una semplice applicazione basata sull'animazione per comprendere il concetto di animazione nel framework Flutter.

  • Crea una nuova applicazione Flutter in Android Studio, product_animation_app.

  • Copia la cartella degli asset da product_nav_app a product_animation_app e aggiungi gli asset all'interno del file pubspec.yaml.

flutter: 
   assets: 
   - assets/appimages/floppy.png 
   - assets/appimages/iphone.png 
   - assets/appimages/laptop.png 
   - assets/appimages/pendrive.png 
   - assets/appimages/pixel.png 
   - assets/appimages/tablet.png
  • Rimuovere il codice di avvio predefinito (main.dart).

  • Aggiungi importazione e funzione principale di base.

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp());
  • Crea il widget MyApp derivato da StatefulWidgtet.

class MyApp extends StatefulWidget { 
   _MyAppState createState() => _MyAppState(); 
}
  • Crea widget _MyAppState e implementa initState e smaltisci oltre al metodo di compilazione predefinito.

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin { 
   Animation<double> animation; 
   AnimationController controller; 
   @override void initState() {
      super.initState(); 
      controller = AnimationController(
         duration: const Duration(seconds: 10), vsync: this
      ); 
      animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller); 
      controller.forward(); 
   } 
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) {
      controller.forward(); 
      return MaterialApp(
         title: 'Flutter Demo',
         theme: ThemeData(primarySwatch: Colors.blue,), 
         home: MyHomePage(title: 'Product layout demo home page', animation: animation,)
      ); 
   } 
   @override 
   void dispose() {
      controller.dispose();
      super.dispose();
   }
}

Qui,

  • Nel metodo initState, abbiamo creato un oggetto controller di animazione (controller), un oggetto di animazione (animazione) e abbiamo avviato l'animazione utilizzando controller.forward.

  • Nel metodo dispose, abbiamo eliminato l'oggetto controller di animazione (controller).

  • Nel metodo build, invia l'animazione al widget MyHomePage tramite il costruttore. Ora, il widget MyHomePage può utilizzare l'oggetto di animazione per animare il suo contenuto.

  • Ora aggiungi il widget ProductBox

class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.name, this.description, this.price, this.image})
      : super(key: key);
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   
   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), 
         height: 140, 
         child: Card( 
            child: Row( 
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
               children: <Widget>[ 
                  Image.asset("assets/appimages/" + image), 
                  Expanded( 
                     child: Container( 
                        padding: EdgeInsets.all(5), 
                        child: Column( 
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[ 
                              Text(this.name, style: 
                                 TextStyle(fontWeight: FontWeight.bold)), 
                              Text(this.description), 
                                 Text("Price: " + this.price.toString()), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      ); 
   }
}
  • Crea un nuovo widget, MyAnimatedWidget per eseguire semplici animazioni in dissolvenza utilizzando l'opacità.

class MyAnimatedWidget extends StatelessWidget { 
   MyAnimatedWidget({this.child, this.animation}); 
      
   final Widget child; 
   final Animation<double> animation; 
   
   Widget build(BuildContext context) => Center( 
   child: AnimatedBuilder(
      animation: animation, 
      builder: (context, child) => Container( 
         child: Opacity(opacity: animation.value, child: child), 
      ), 
      child: child), 
   ); 
}
  • Qui abbiamo usato AniatedBuilder per fare la nostra animazione. AnimatedBuilder è un widget che costruisce il suo contenuto mentre fa l'animazione allo stesso tempo. Accetta un oggetto di animazione per ottenere il valore di animazione corrente. Abbiamo usato il valore di animazione, animation.value per impostare l'opacità del widget figlio. In effetti, il widget animerà il widget figlio utilizzando il concetto di opacità.

  • Infine, crea il widget MyHomePage e utilizza l'oggetto di animazione per animare qualsiasi suo contenuto.

class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title, this.animation}) : super(key: key); 
   
   final String title; 
   final Animation<double> 
   animation; 
   
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text("Product Listing")),body: ListView(
            shrinkWrap: true,
            padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget>[
               FadeTransition(
                  child: ProductBox(
                     name: "iPhone", 
                     description: "iPhone is the stylist phone ever", 
                     price: 1000, 
                     image: "iphone.png"
                  ), opacity: animation
               ), 
               MyAnimatedWidget(child: ProductBox(
                  name: "Pixel", 
                  description: "Pixel is the most featureful phone ever", 
                  price: 800, 
                  image: "pixel.png"
               ), animation: animation), 
               ProductBox(
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox(
                  name: "Tablet", 
                  description: "Tablet is the most useful device ever for meeting", 
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox(
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ),
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ),
            ],
         )
      );
   }
}

Qui, abbiamo usato FadeAnimation e MyAnimationWidget per animare i primi due elementi nell'elenco. FadeAnimation è una classe di animazione incorporata, che abbiamo utilizzato per animare il suo figlio utilizzando il concetto di opacità.

  • Il codice completo è il seguente:

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatefulWidget { 
   _MyAppState createState() => _MyAppState(); 
} 
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
   Animation<double> animation; 
   AnimationController controller; 
   
   @override 
   void initState() {
      super.initState(); 
      controller = AnimationController(
         duration: const Duration(seconds: 10), vsync: this); 
      animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller); 
      controller.forward(); 
   } 
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) {
      controller.forward(); 
      return MaterialApp( 
         title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue,), 
         home: MyHomePage(title: 'Product layout demo home page', animation: animation,) 
      ); 
   } 
   @override 
   void dispose() {
      controller.dispose();
      super.dispose(); 
   } 
}
class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title, this.animation}): super(key: key);
   final String title; 
   final Animation<double> animation; 
   
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text("Product Listing")), 
         body: ListView(
            shrinkWrap: true, 
            padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget>[
               FadeTransition(
                  child: ProductBox(
                     name: "iPhone", 
                     description: "iPhone is the stylist phone ever", 
                     price: 1000, 
                     image: "iphone.png"
                  ), 
                  opacity: animation
               ), 
               MyAnimatedWidget(
                  child: ProductBox( 
                     name: "Pixel", 
                     description: "Pixel is the most featureful phone ever", 
                     price: 800, 
                     image: "pixel.png"
                  ), 
                  animation: animation
               ), 
               ProductBox( 
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox(
                  name: "Tablet",
                  description: "Tablet is the most useful device ever for meeting",
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox(
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ), 
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ), 
            ], 
         )
      ); 
   } 
} 
class ProductBox extends StatelessWidget { 
   ProductBox({Key key, this.name, this.description, this.price, this.image}) :
      super(key: key);
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), 
         height: 140, 
         child: Card(
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
               children: <Widget>[ 
                  Image.asset("assets/appimages/" + image), 
                  Expanded(
                     child: Container( 
                        padding: EdgeInsets.all(5), 
                        child: Column( 
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[ 
                              Text(
                                 this.name, style: TextStyle(
                                    fontWeight: FontWeight.bold
                                 )
                              ), 
                              Text(this.description), Text(
                                 "Price: " + this.price.toString()
                              ), 
                           ], 
                        )
                     )
                  ) 
               ]
            )
         )
      ); 
   } 
}
class MyAnimatedWidget extends StatelessWidget { 
   MyAnimatedWidget({this.child, this.animation}); 
   final Widget child; 
   final Animation<double> animation; 
 
   Widget build(BuildContext context) => Center( 
      child: AnimatedBuilder(
         animation: animation, 
         builder: (context, child) => Container( 
            child: Opacity(opacity: animation.value, child: child), 
         ), 
         child: child
      ), 
   ); 
}
  • Compila ed esegui l'applicazione per vedere i risultati. La versione iniziale e finale dell'applicazione è la seguente: