2021 年,你的Flutter动画还在用 setState ?
合成位
、绘画
、合成
。每当界面发生变化时,帧触发器就会更新结果。如下图所示,每两个网格代表一帧的 UI 时间(左)和 Raster 时间(右)。当左边很高的时候,说明你的界面写法有问题。看下面两个UI框架,可以看到Build占了很大一部分,说明UI上可能存在一些效率低下的地方。 可以俯视整个Build遍历的深度。如果树太深,可能会出现问题。此时,您应该查看是否更新了任何不必要的部分。
但要注意,对 动画如下。中间的圆形渐变展开动画,上下方块不动。 程序入口 在 为了方便测试,中间成分在Shower中提取出来。使用 然后你会发现 要做的改动:1.去掉监听动画器2.使用 就这样,我们来看看效果,看动画控制台有没有什么。是不是太多了?这不是打我脸 从下面的UI框架可以看出,在同样的场景下,使用 首先 在 这么一看,好像 作者:张凤杰特烈全局主题、文本
等的更新将不可避免地从顶部节点开始遍历。这是不可避免的。虽然会造成一些延迟,但这些都是视觉不敏感的操作
,操作次数也不是很频繁
。但当涉及到动画时,情况就不同了。如果丢掉一些镜头,就会感觉像卡卡的,不顺利。另一方面,动画会持续渲染一段时间,所以需要特别注意性能问题。另外,不要在调试模式下查看性能
、不要在调试模式下查看性能
、不是de中的性能!使用
配置文件
模式。 2。负面学习材料! ! !
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage());
}
}
复制代码
SingleTickerProviderStateMixin动画师控制
中混合_HomePageState
,监控动画师并在每次触发时调用 _HomePageState。 的
和子组件setState
方法用于更新_HomePageState
中的元素。单击中心时会触发动画。 class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
lowerBound: 0.3,
upperBound: 1.0,
vsync: this,
duration: const Duration(milliseconds: 500));
controller.addListener(() {
setState(() {});
});
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
print('---------_HomePageState#build------');
return Scaffold(
appBar: AppBar(
title: Text("动画测试"),
),
body: Column(
children: [
Expanded(
child: Padding( padding: EdgeInsets.only(top: 20),
child: buildBoxes(),
),
),
Expanded(
child: Center(
child: buildCenter(),
),
),
Expanded(
child: Padding( padding: EdgeInsets.only(bottom: 20),
child: buildBoxes(),
),
),
],
));
}
Widget buildCenter() => GestureDetector(
onTap: () {
controller.forward(from: 0.3);
},
child: Transform.scale(
scale: controller.value,
child: Opacity(opacity: controller.value, child: Shower()),
),
);
Widget buildBoxes() => Wrap(
spacing: 20,
runSpacing: 20,
children: List.generate( 24,
(index) => Container(
alignment: Alignment.center,
width: 40,
height: 40,
color: Colors.orange,
child: Text('$index',style: TextStyle(color: Colors.white),),
)),
);
}
复制代码
StatefulWidget
可以在运行动画时轻松测试 _ShowerState
回调函数。 class Shower extends StatefulWidget {
@override
_ShowerState createState() => _ShowerState();
}
class _ShowerState extends State<Shower> {
@override
void initState() {
super.initState();
print('-----Shower#initState----------');
}
@override
Widget build(BuildContext context) {
print('-----Shower#build----------');
return Container(
width: 150,
height: 150,
alignment: Alignment.center,
decoration: BoxDecoration(color: Colors.orange, shape: BoxShape.circle),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
height: 30,
width: 30,
decoration:
BoxDecoration(color: Colors.white, shape: BoxShape.circle),
),
Container(
height: 30,
width: 30,
decoration:
BoxDecoration(color: Colors.white, shape: BoxShape.circle),
)
],
),
Text(
'Toly',
style: TextStyle(fontSize: 40, color: Colors.white),
),
],
),
);
}
}
复制代码
_HomePageState#build
和Shower#build
会持续射击。根本原因是setState
在更高层次上执行,导致下面的树被遍历。在这种情况下,不建议执行动画。我们需要做的是降低更新元素节点级别。 Flutter 为我们提供了 AnimatedBuilder
。 3。正面教材
AnimatedBuilder
AnimatedBuilder
@override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
lowerBound: 0.3,
upperBound: 1.0,
duration: const Duration(milliseconds: 500)); // 1、移除监听动画器
}
Widget buildCenter() => GestureDetector(
onTap: () {
controller.forward(from: 0);
},
child: AnimatedBuilder( // 2、使用 AnimatedBuilder
animation: controller,
builder: (ctx, child) {
return Transform.scale(
scale: controller.value,
child: Opacity(opacity: controller.value, child: child),
);
},
child: Shower()),
);
复制代码
setState
吗? AnimatedBuilder
进行动画可以有效缩短Build。
,成员包括构造函数 builder4.
AnimatedBuilder
源码分析AnimatedBuilder
继承了。
还需要创建对象可监听
对象动画
。 class AnimatedBuilder extends AnimatedWidget {
const AnimatedBuilder({
Key key,
@required Listenable animation,
@required this.builder,
this.child,
}) : assert(animation != null),
assert(builder != null),
super(key: key, listenable: animation);
final TransitionBuilder builder;
final Widget child;
@override
Widget build(BuildContext context) {
return builder(context, child);
}
}
typedef TransitionBuilder = Widget Function(BuildContext context, Widget child);
复制代码
AnimatedBuilder
很简单,核心应该在AnimatedWidget
。可以看出,AnimatedWidget
是一个StatefulWidget
,需要更改其状态。 abstract class AnimatedWidget extends StatefulWidget {
const AnimatedWidget({
Key key,
@required this.listenable,
}) : assert(listenable != null),
super(key: key);
final Listenable listenable;
@protected
Widget build(BuildContext context);
@override
_AnimatedState createState() => _AnimatedState();
}
复制代码
_AnimatedState
中也非常容易处理,监听传入的listenable
,执行e和更改_handleChange 完成。 ......,没错:
时,执行的是 你叔叔终究还是你叔叔
。更新仍然依赖于setState
。但相比上面的setState,这里setState
的影响要小很多。 U 执行 iBuildwidget.build(context),即当前上下文调整为
方法执行:widget.build
方法,并且 .buildbuilder (context, child)
,这是我们编写的builder(下图 仍然是即将到来的
child
。这不会构建新的淋浴间组件,也不会触发淋浴组件状态对应的 build 方法,所有动画需求都在 Builder
方法中使用,更新的东西也被 AnimatedBuilder 部分包裹.这样
岁月静好
.@override
Widget build(BuildContext context) {
return builder(context, child);
}
复制代码
AnimatedBuilder
没有什么神秘的。了解了这一点后,再看看Flutter框架中封装的各种动画组件,你就恍然大悟了,这就是知一知百
,总结一下,不是setState
是不好,但使用它的时机是正确的。 AnimatedBuilder
本质上是通过setState
来触发更新的,所以看问题时不要片面激进。对于应用UI来说,我们需要关注的是如何最大限度地减少Build
过程的消耗,尤其是像动画、滑动这样会持续渲染的场景。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。