[Flutter][性能优化]篇二:图像和动画优化

图像优化

1.选择合适的图片格式

SVG 是矢量图形格式,因此可以无损地缩放而不会失真。 PNG 是位图格式,它使用像素网格来表示图像,当被放大时,会出现像素锯齿或失真。 SVG 适用于需要可伸缩性和交互性的图像,特别是图标、矢量图形和动画。PNG 适用于静态的位图图像,特别是照片、复杂的图像或需要保留细节的图像。选择使用哪种格式取决于你的需求和使用场景。

SVG图片体积小,可以减少内存占用。

2.适当的缓存图片

某些网络图片需要被频繁展示时,可以选择将其缓存,避免消耗性能。 例如

js 复制代码
return CachedNetworkImage( 
imageUrl: imageUrl, 
placeholder: (context, url) => CircularProgressIndicator(), 
errorWidget: (context, url, error) => Icon(Icons.error), 
cacheManager: DefaultCacheManager(), // 使用默认的缓存管理器 
);

动画优化

1.使用AnimatiedContainer、AnimatedOpacity等动画组件

这些组件将动画操作交给了GPU处理,而不是CPU,实现了硬件加速。

2.使用AnimatedBuilder组件减小重绘区域

只有当需要更新的部分发生改变时,才进行重绘操作。在使用动画时,确保只对需要动画的部分进行更新,避免整个页面或组件的重绘。可以使用AnimatedBuilder组件来尽可能地减小重绘区域。

示例代码

js 复制代码
class ExampleWidget extends StatefulWidget {
  @override
  _ExampleWidgetState createState() => _ExampleWidgetState();
}

class _ExampleWidgetState extends State<ExampleWidget> with TickerProviderStateMixin {
  AnimationController _animationController;
  Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      vsync: this,
      duration: Duration(seconds: 1),
    );
    _animation = Tween<double>(begin: 0, end: 1).animate(_animationController);
    _animationController.repeat(reverse: true);
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: AnimatedBuilder(
        animation: _animation,
        builder: (context, child) {
          return Opacity(
            opacity: _animation.value,
            child: Container(
              width: 200,
              height: 200,
              color: Colors.blue,
              child: Center(
                child: Text(
                  'Hello, world!',
                  style: TextStyle(fontSize: 24, color: Colors.white),
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

在上面的代码中,我们通过AnimationControllerTween来创建一个循环渐隐循环出现的动画效果。这时候,如果我们直接将Opacity组件放到ColumnRow组件中,整个界面都会被重绘,性能开销较大。为了减小重绘区域,我们使用了AnimatedBuilder组件来尽可能地减少重绘区域。在builder回调中,我们将要进行动画操作的组件放到Opacity组件内部并将其返回,这样只会对Opacity和其子组件(即上面例子中的Container)进行重绘操作,大大提高了性能。

3.使用LayoutBuilderConstrainedBox等组件来预先计算布局,避免过多的布局计算

在动画过程中,频繁的布局计算会导致性能下降。可以通过使用LayoutBuilderConstrainedBox等组件来预先计算布局,并尽量避免在动画过程中执行复杂的布局操作。

js 复制代码
class ExampleWidget extends StatefulWidget {
  @override
  _ExampleWidgetState createState() => _ExampleWidgetState();
}

class _ExampleWidgetState extends State<ExampleWidget> with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      vsync: this,
      duration: Duration(seconds: 1),
    );
    _animation = Tween<double>(begin: 0, end: 1).animate(_animationController);
    _animationController.repeat(reverse: true);
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: LayoutBuilder(
        builder: (context, constraints) {
          final size = constraints.biggest;
          return ConstrainedBox(
            constraints: BoxConstraints.tight(size),
            child: AnimatedBuilder(
              animation: _animation,
              builder: (context, child) {
                return Stack(
                  children: [
                    Positioned(
                      top: size.height * _animation.value,
                      child: Container(
                        width: size.width,
                        height: size.height,
                        color: Colors.blue,
                      ),
                    ),
                    Positioned(
                      top: size.height * (1 - _animation.value),
                      child: Container(
                        width: size.width,
                        height: size.height,
                        color: Colors.red,
                      ),
                    ),
                  ],
                );
              },
            ),
          );
        },
      ),
    );
  }
}

在上述的代码中,LayoutBuilderbuilder回调会在初始阶段进行一次计算,并获取到父组件给予的最大约束。然后,将这个约束作为ConstrainedBox的约束条件,使得ConstrainedBox的大小固定为这个约束。

由于ConstrainedBox的大小是固定的,即使在动画过程中AnimatedBuilder内部的布局发生变化,ConstrainedBox的大小也不会改变。这意味着AnimatedBuilder的子组件在每一帧中的位置只需基于固定的大小进行计算,而不需要重新计算整个布局。

因此,通过使用LayoutBuilderConstrainedBox来预先计算布局,可以避免在动画过程中重复执行复杂的布局计算,从而提高性能。

4.即时释放动画资源

如果动画没有被关闭,它会继续在后台执行,占用资源。 为了避免这些问题,应该在页面关闭时停止动画并清除相关资源。可以通过在dispose()方法中释放资源的方式来实现。具体来说,可以使用以下代码在dispose()方法中停止动画并释放相关资源:

js 复制代码
@override
void dispose() {
  _animationController.dispose();   // 停止动画
  super.dispose();
}

_animationController.dispose()方法会停止动画并释放相关的资源。同时,需要确保调用父类的dispose()方法以释放其他资源。

相关推荐
恋猫de小郭39 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端