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。正面教材
AnimatedBuilderAnimatedBuilder@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前端网发表,如需转载,请注明页面地址。
code前端网



发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。