Flutter - Animação
A animação é um procedimento complexo em qualquer aplicativo móvel. Apesar de sua complexidade, o Animation aprimora a experiência do usuário a um novo nível e fornece uma interação rica do usuário. Devido à sua riqueza, a animação torna-se parte integrante da aplicação móvel moderna. A estrutura Flutter reconhece a importância da animação e fornece uma estrutura simples e intuitiva para desenvolver todos os tipos de animações.
Introdução
Animação é um processo de mostrar uma série de imagens / imagens em uma ordem particular dentro de uma duração específica para dar uma ilusão de movimento. Os aspectos mais importantes da animação são os seguintes -
A animação tem dois valores distintos: valor inicial e valor final. A animação começa no valor inicial e passa por uma série de valores intermediários e, finalmente, termina nos valores finais. Por exemplo, para animar um widget para desaparecer, o valor inicial será a opacidade total e o valor final será a opacidade zero.
Os valores intermediários podem ser lineares ou não lineares (curva) por natureza e podem ser configurados. Entenda que a animação funciona como está configurada. Cada configuração fornece uma sensação diferente para a animação. Por exemplo, o esmaecimento de um widget será de natureza linear, enquanto o salto de uma bola será de natureza não linear.
A duração do processo de animação afeta a velocidade (lentidão ou rapidez) da animação.
A capacidade de controlar o processo de animação, como iniciar a animação, interromper a animação, repetir a animação para definir o número de vezes, reverter o processo de animação, etc.,
No Flutter, o sistema de animação não faz nenhuma animação real. Em vez disso, ele fornece apenas os valores necessários em cada quadro para renderizar as imagens.
Aulas baseadas em animação
O sistema de animação Flutter é baseado em objetos de animação. As principais classes de animação e seu uso são as seguintes -
Animação
Gera valores interpolados entre dois números ao longo de uma certa duração. As aulas de animação mais comuns são -
Animation<double> - interpolar valores entre dois números decimais
Animation<Color> - interpolar cores entre duas cores
Animation<Size> - interpolar tamanhos entre dois tamanhos
AnimationController- Objeto de animação especial para controlar a própria animação. Ele gera novos valores sempre que o aplicativo está pronto para um novo quadro. Suporta animação de base linear e o valor começa de 0,0 a 1,0
controller = AnimationController(duration: const Duration(seconds: 2), vsync: this);
Aqui, o controlador controla a animação e a opção de duração controla a duração do processo de animação. vsync é uma opção especial usada para otimizar o recurso usado na animação.
Animação Curvada
Semelhante ao AnimationController, mas suporta animação não linear. CurvedAnimation pode ser usado junto com o objeto Animation como abaixo -
controller = AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = CurvedAnimation(parent: controller, curve: Curves.easeIn)
Tween <T>
Derivado de Animatable <T> e usado para gerar números entre quaisquer dois números diferentes de 0 e 1. Ele pode ser usado junto com o objeto Animation usando o método animate e passando o objeto Animation real.
AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this); Animation<int> customTween = IntTween(
begin: 0, end: 255).animate(controller);
Tween também pode ser usado junto com CurvedAnimation como abaixo -
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);
Aqui, o controlador é o controlador de animação real. curve fornece o tipo de não linearidade e customTween fornece uma faixa personalizada de 0 a 255.
Fluxo de trabalho da Animação Flutter
O fluxo de trabalho da animação é o seguinte -
Defina e inicie o controlador de animação no initState do StatefulWidget.
AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = Tween<double>(begin: 0, end: 300).animate(controller);
controller.forward();
Adicione ouvinte baseado em animação, addListener para alterar o estado do widget.
animation = Tween<double>(begin: 0, end: 300).animate(controller) ..addListener(() {
setState(() {
// The state that has changed here is the animation object’s value.
});
});
Widgets embutidos, AnimatedWidget e AnimatedBuilder podem ser usados para pular esse processo. Ambos os widgets aceitam o objeto Animation e obtêm os valores atuais necessários para a animação.
Obtenha os valores da animação durante o processo de construção do widget e, em seguida, aplique-os para largura, altura ou qualquer propriedade relevante em vez do valor original.
child: Container(
height: animation.value,
width: animation.value,
child: <Widget>,
)
Aplicação de Trabalho
Vamos escrever um aplicativo baseado em animação simples para entender o conceito de animação no framework Flutter.
Crie um novo aplicativo Flutter no Android Studio, product_animation_app.
Copie a pasta de ativos de product_nav_app para product_animation_app e inclua ativos dentro do arquivo 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
Remova o código de inicialização padrão (main.dart).
Adicionar importação e função principal básica.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
Crie o widget MyApp derivado de StatefulWidgtet.
class MyApp extends StatefulWidget {
_MyAppState createState() => _MyAppState();
}
Crie o widget _MyAppState e implemente initState e descarte além do método de construção padrão.
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();
}
}
Aqui,
No método initState, criamos um objeto controlador de animação (controlador), um objeto de animação (animação) e iniciamos a animação usando controller.forward.
No método dispose, descartamos o objeto controlador de animação (controlador).
No método de construção, envie a animação para o widget MyHomePage por meio do construtor. Agora, o widget MyHomePage pode usar o objeto de animação para animar seu conteúdo.
Agora, adicione o 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()),
],
)
)
)
]
)
)
);
}
}
Crie um novo widget, MyAnimatedWidget para fazer uma animação de fade simples usando opacidade.
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),
);
}
Aqui, usamos o AniatedBuilder para fazer nossa animação. AnimatedBuilder é um widget que constrói seu conteúdo enquanto faz a animação ao mesmo tempo. Ele aceita um objeto de animação para obter o valor da animação atual. Usamos o valor da animação, animation.value, para definir a opacidade do widget filho. Na verdade, o widget irá animar o widget filho usando o conceito de opacidade.
Por fim, crie o widget MyHomePage e use o objeto de animação para animar qualquer um de seu conteúdo.
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"
),
],
)
);
}
}
Aqui, usamos FadeAnimation e MyAnimationWidget para animar os dois primeiros itens da lista. FadeAnimation é uma classe de animação integrada, que usamos para animar seu filho usando o conceito de opacidade.
O código completo é o seguinte -
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
),
);
}
Compile e execute o aplicativo para ver os resultados. A versão inicial e final do aplicativo é a seguinte -