flutter animation 动画——迹忆客-ag捕鱼王app官网
在任何移动应用程序中,动画都是一个复杂的过程。 尽管它很复杂,但动画将用户体验提升到了一个新的水平,并提供了丰富的用户交互。 由于其丰富性,动画成为现代移动应用程序不可或缺的一部分。 flutter 框架认识到动画的重要性,并提供了一个简单直观的框架来开发所有类型的动画。
介绍
动画是在特定持续时间内以特定顺序显示一系列图像/图片以产生运动错觉的过程。动画最重要的方面如下 -
- 动画有两个不同的值:开始值和结束值。动画从start值开始,经过一系列中间值,最后在 end 值结束。例如,要使小部件动画消失,初始值将是完全不透明度,最终值将是零不透明度。
- 中间值本质上可以是线性的或非线性的(曲线),并且可以配置。了解动画按配置工作。每种配置都为动画提供了不同的感觉。例如,小部件的淡化本质上是线性的,而球的弹跳本质上是非线性的。
- 动画过程的持续时间会影响动画的速度(慢或快)。
- 控制动画过程的能力,如开始动画、停止动画、重复动画以设置次数、反转动画过程等,
- 在 flutter 中,动画系统不做任何真正的动画。相反,它只提供每帧渲染图像所需的值。
基于动画的类
flutter 动画系统是基于 animation 对象的。核心动画类及其用法如下 -
动画
在特定持续时间内生成两个数字之间的插值。最常见的动画类是 -
animation
- 在两个十进制数之间插入值animation
- 在两种颜色之间插入颜色animation
- 在两个尺寸之间插入尺寸animationcontroller
- 用于控制动画本身的特殊动画对象。每当应用程序为新帧做好准备时,它都会生成新值。它支持基于线性的动画,值从 0.0 到 1.0
在这里,控制器控制动画和持续时间选项控制动画过程的持续时间。vsync 是一个特殊选项,用于优化动画中使用的资源。controller = animationcontroller(duration: const duration(seconds: 2), vsync: this);
曲线动画
类似于 animationcontroller 但支持非线性动画。curvedanimation 可以与 animation 对象一起使用,如下所示
controller = animationcontroller(duration: const duration(seconds: 2), vsync: this);
animation = curvedanimation(parent: controller, curve: curves.easein)
tween
派生自 animatable
,用于生成 0 和 1 以外的任意两个数字之间的数字。它可以通过使用 animate 方法并传递实际的 animation 对象与 animation 对象一起使用。
animationcontroller controller = animationcontroller(
duration: const duration(milliseconds: 1000),
vsync: this); animation customtween = inttween(
begin: 0, end: 255).animate(controller);
tween 也可以与 curvedanimation 一起使用,如下所示 -
animationcontroller controller = animationcontroller(
duration: const duration(milliseconds: 500), vsync: this);
final animation curve = curvedanimation(parent: controller, curve: curves.easeout);
animation customtween = inttween(begin: 0, end: 255).animate(curve);
这里,控制器是实际的动画控制器。curve 提供非线性类型,customtween 提供从 0 到 255 的自定义范围。
flutter 动画的工作流程
动画的工作流程如下 -
在 statefulwidget 的 initstate 中定义并启动动画控制器。
animationcontroller(duration: const duration(seconds: 2), vsync: this);
animation = tween(begin: 0, end: 300).animate(controller);
controller.forward();
添加基于动画的侦听器,addlistener 以更改小部件的状态。
animation = tween(begin: 0, end: 300).animate(controller) ..addlistener(() {
setstate(() {
// the state that has changed here is the animation object’s value.
});
});
内置小部件 animatedwidget 和 animatedbuilder 可用于跳过此过程。两个小部件都接受 animation 对象并获取动画所需的当前值。
在小部件的构建过程中获取动画值,然后将其应用于宽度、高度或任何相关属性,而不是原始值。
child: container(
height: animation.value,
width: animation.value,
child: ,
)
应用程序
让我们编写一个简单的基于动画的应用程序来了解 flutter 框架中的动画概念。
在 android studio 中新建一个flutter应用程序 product_animation_app。
将资产文件夹从 product_nav_app 复制到 product_animation_app 并在 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
删除默认启动代码 (main.dart)。
添加导入和基本的主要功能。
import 'package:flutter/material.dart';
void main() => runapp(myapp());
创建从 statefulwidgtet 派生的 myapp 小部件。
class myapp extends statefulwidget {
_myappstate createstate() => _myappstate();
}
除了默认构建方法之外,创建 _myappstate
小部件并实现 initstate
和 dispose
。
class _myappstate extends state with singletickerproviderstatemixin {
animation animation;
animationcontroller controller;
@override void initstate() {
super.initstate();
controller = animationcontroller(
duration: const duration(seconds: 10), vsync: this
);
animation = tween(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();
}
}
这里
在 initstate
方法中,我们创建了一个动画控制器对象(controller),一个动画对象(animation)并使用controller.forward 启动动画。
在 dispose 方法中,我们已经对动画控制器对象(controller)进行了处理。
在 build 方法中,通过构造函数将动画发送到 myhomepage 小部件。现在,myhomepage 小部件可以使用动画对象为其内容设置动画。
现在,添加 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: [
image.asset("assets/appimages/" image),
expanded(
child: container(
padding: edgeinsets.all(5),
child: column(
mainaxisalignment: mainaxisalignment.spaceevenly,
children: [
text(this.name, style:
textstyle(fontweight: fontweight.bold)),
text(this.description),
text("price: " this.price.tostring()),
],
)
)
)
]
)
)
);
}
}
创建一个新的小部件 myanimatedwidget
来使用不透明度做简单的淡入淡出动画。
class myanimatedwidget extends statelesswidget {
myanimatedwidget({this.child, this.animation});
final widget child;
final animation animation;
widget build(buildcontext context) => center(
child: animatedbuilder(
animation: animation,
builder: (context, child) => container(
child: opacity(opacity: animation.value, child: child),
),
child: child),
);
}
在这里,我们使用了 animatedbuilder 来制作动画。animatedbuilder 是一个小部件,它在制作动画的同时构建其内容。它接受一个动画对象来获取当前动画值。我们使用了动画值,animation.value 来设置子部件的不透明度。实际上,小部件将使用不透明度概念为子小部件设置动画。
最后,创建 myhomepage 小部件并使用动画对象为其任何内容设置动画。
class myhomepage extends statelesswidget {
myhomepage({key key, this.title, this.animation}) : super(key: key);
final string title;
final animation
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: [
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"
),
],
)
);
}
}
在这里,我们使用 fadeanimation 和 myanimationwidget 为列表中的前两项设置动画。fadeanimation 是一个内置动画类,我们曾经使用不透明度概念为其子动画制作动画。
完整的代码如下
import 'package:flutter/material.dart';
void main() => runapp(myapp());
class myapp extends statefulwidget {
_myappstate createstate() => _myappstate();
}
class _myappstate extends state with singletickerproviderstatemixin {
animation animation;
animationcontroller controller;
@override
void initstate() {
super.initstate();
controller = animationcontroller(
duration: const duration(seconds: 10), vsync: this);
animation = tween(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 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: [
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: [
image.asset("assets/appimages/" image),
expanded(
child: container(
padding: edgeinsets.all(5),
child: column(
mainaxisalignment: mainaxisalignment.spaceevenly,
children: [
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 animation;
widget build(buildcontext context) => center(
child: animatedbuilder(
animation: animation,
builder: (context, child) => container(
child: opacity(opacity: animation.value, child: child),
),
child: child
),
);
}
编译并运行应用程序以查看结果。该应用程序的初始和最终版本如下