[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()方法以释放其他资源。

相关推荐
送鱼的老默28 分钟前
学习笔记--入门typescript直接案例开搞
前端·typescript
Prometheus28 分钟前
从 XMLHttpRequest 到 fetch、ReadableStream、SSE、EventSource:前端流式通信完整梳理
前端
光影少年34 分钟前
useEffect 完整理解:依赖数组、副作用清理、模拟生命周期
前端·react.js·程序员
之歆1 小时前
DAY_18深度解析:数据类型转换与运算符全攻略(上)
前端·javascript
大家的林语冰1 小时前
pnpm 11 发布,弃用 JSON 和 npm CLI,进化为纯 ES6 模块,新增 pnpm pack-app 等命令,供应链保护默认启用,要求 Node
前端·javascript·node.js
漓漾li1 小时前
每日面试题-前端2
前端·react.js·面试
Alice-YUE1 小时前
深入解析 JS 事件循环:浏览器与 Node.js 的差异全解析
前端·javascript·笔记·学习
HYCS1 小时前
用pixijs实现fabricjs(二):对象的基础位置信息
前端·javascript·canvas
淸湫1 小时前
项目中使用了全局权限管理,请详细描述如何通过Vue Router的路由守卫来实现全局权限控制?
前端·vue.js
雪铃儿1 小时前
Shorebird 之外,Flutter Android 热更新还有什么选择
android·前端