图像优化
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),
),
),
),
);
},
),
);
}
}
在上面的代码中,我们通过AnimationController
和Tween
来创建一个循环渐隐循环出现的动画效果。这时候,如果我们直接将Opacity
组件放到Column
或Row
组件中,整个界面都会被重绘,性能开销较大。为了减小重绘区域,我们使用了AnimatedBuilder
组件来尽可能地减少重绘区域。在builder
回调中,我们将要进行动画操作的组件放到Opacity
组件内部并将其返回,这样只会对Opacity
和其子组件(即上面例子中的Container
)进行重绘操作,大大提高了性能。
3.使用LayoutBuilder
或ConstrainedBox
等组件来预先计算布局,避免过多的布局计算
在动画过程中,频繁的布局计算会导致性能下降。可以通过使用LayoutBuilder
或ConstrainedBox
等组件来预先计算布局,并尽量避免在动画过程中执行复杂的布局操作。
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,
),
),
],
);
},
),
);
},
),
);
}
}
在上述的代码中,LayoutBuilder
的builder
回调会在初始阶段进行一次计算,并获取到父组件给予的最大约束。然后,将这个约束作为ConstrainedBox
的约束条件,使得ConstrainedBox
的大小固定为这个约束。
由于ConstrainedBox
的大小是固定的,即使在动画过程中AnimatedBuilder
内部的布局发生变化,ConstrainedBox
的大小也不会改变。这意味着AnimatedBuilder
的子组件在每一帧中的位置只需基于固定的大小进行计算,而不需要重新计算整个布局。
因此,通过使用LayoutBuilder
和ConstrainedBox
来预先计算布局,可以避免在动画过程中重复执行复杂的布局计算,从而提高性能。
4.即时释放动画资源
如果动画没有被关闭,它会继续在后台执行,占用资源。 为了避免这些问题,应该在页面关闭时停止动画并清除相关资源。可以通过在dispose()
方法中释放资源的方式来实现。具体来说,可以使用以下代码在dispose()
方法中停止动画并释放相关资源:
js
@override
void dispose() {
_animationController.dispose(); // 停止动画
super.dispose();
}
_animationController.dispose()
方法会停止动画并释放相关的资源。同时,需要确保调用父类的dispose()
方法以释放其他资源。