Code前端首页关于Code前端联系我们

2021 年,你的Flutter动画还在用 setState ?

terry 2年前 (2023-09-22) 阅读数 70 #移动小程序
2021 年你的 Flutter 动画还在使用 setState 吗? ? 、合成位绘画合成。每当界面发生变化时,帧触发器就会更新结果。如下图所示,每两个网格代表一帧的 UI 时间(左)和 Raster 时间(右)。当左边很高的时候,说明你的界面写法有问题。看下面两个UI框架,可以看到Build占了很大一部分,说明UI上可能存在一些效率低下的地方。

2021 年,你的Flutter动画还在用 setState ?

2021 年,你的Flutter动画还在用 setState ?


可以俯视整个Build遍历的深度。如果树太深,可能会出现问题。此时,您应该查看是否更新了任何不必要的部分。

2021 年,你的Flutter动画还在用 setState ?


但要注意,对全局主题、文本等的更新将不可避免地从顶部节点开始遍历。这是不可避免的。虽然会造成一些延迟,但这些都是视觉不敏感的操作,操作次数也不是很频繁。但当涉及到动画时,情况就不同了。如果丢掉一些镜头,就会感觉像卡卡的,不顺利。另一方面,动画会持续渲染一段时间,所以需要特别注意性能问题。另外,不要在调试模式下查看性能不要在调试模式下查看性能不是de中的性能!使用配置文件模式。

2021 年,你的Flutter动画还在用 setState ?


2。负面学习材料! ! !

动画如下。中间的圆形渐变展开动画,上下方块不动。

2021 年,你的Flutter动画还在用 setState ?

程序入口

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),),
                )),
      );
}
复制代码

为了方便测试,中间成分在Shower中提取出来。使用 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#buildShower#build会持续射击。根本原因是setState在更高层次上执行,导致下面的树被遍历。在这种情况下,不建议执行动画。我们需要做的是降低更新元素节点级别。 Flutter 为我们提供了 AnimatedBuilder

2021 年,你的Flutter动画还在用 setState ?

2021 年,你的Flutter动画还在用 setState ?


3。正面教材AnimatedBuilder

要做的改动:1.去掉监听动画器2.使用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吗?

2021 年,你的Flutter动画还在用 setState ?

从下面的UI框架可以看出,在同样的场景下,使用AnimatedBuilder进行动画可以有效缩短Build。

2021 年,你的Flutter动画还在用 setState ?

2021 年,你的Flutter动画还在用 setState ?


4.AnimatedBuilder源码分析

首先AnimatedBuilder继承了

,成员包括构造函数 builder

和子组件还需要创建对象可监听对象动画

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 执行 iBuild 时,执行的是 widget.build(context),即当前上下文调整为 widget.build 方法,并且 .build方法执行:builder (context, child),这是我们编写的builder(下图 仍然是即将到来的 child。这不会构建新的淋浴间组件,也不会触发淋浴组件状态对应的 build 方法,所有动画需求都在 Builder 方法中使用,更新的东西也被 AnimatedBuilder 部分包裹.这样岁月静好.

@override
Widget build(BuildContext context) {
  return builder(context, child);
}
复制代码

2021 年,你的Flutter动画还在用 setState ?


这么一看,好像AnimatedBuilder没有什么神秘的。了解了这一点后,再看看Flutter框架中封装的各种动画组件,你就恍然大悟了,这就是知一知百,总结一下,不是setState是不好,但使用它的时机是正确的。 AnimatedBuilder本质上是通过setState来触发更新的,所以看问题时不要片面激进。对于应用UI来说,我们需要关注的是如何最大限度地减少Build过程的消耗,尤其是像动画、滑动这样会持续渲染的场景。

作者:张凤杰特烈

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

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

热门