例子 1、
现有一个滑动聊天页面出现/隐藏jump按钮的需求:
- 优化前:ListView外层setState实现:
class _ChatListViewWidgetState extends State {
ScrollController scrollController;
double _chatListOffset = 0;
@override
void initState() {
super.initState();
scrollController = new ScrollController()
..addListener(() {
setState(() {
_chatListOffset = scrollController.offset;
});
});
}
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.bottomCenter,
children: [
ListView.builder(
controller: _scrollController,
...
),
_chatListOffset > 150 ? Image.asset('jump.png') : SizedBox()
],
);
}
}
- 优化后:在JumpButton内部setState实现
class JumpButton extends StatefulWidget {
ScrollController scrollController;
JumpButton({this.scrollController});
@override
_JumpButtonState createState() => _JumpButtonState();
}
class _JumpButtonState extends State {
double _chatListOffset = 0;
@override
void initState() {
super.initState();
widget.scrollController.addListener(() {
setState(() {
_chatListOffset = widget.scrollController.offset;
});
});
}
@override
Widget build(BuildContext context) {
return _chatListOffset > 150 ? Image.asset('jump.png') : SizedBox();
}
}
- 开启 DevTools 的 Repaint RainBow差别:
左边是优化前: 发现在滑动过程中,ListView在不断的绘制,右边为优化后,在按钮出现的时候绘制一次
- 思考与总结:
setState会导致当前页面根节点开始遍历,所以我们要在需要重新渲染的Widget内部进行setState更新自己,可以有效防止多余的树节点遍历。参考flutter官方的性能测试视频也有相应的讲解:https://www.bilibili.com/video/BV1F4411D7rP 的13’开始
例子 2、
现有一个在聊天页面上层悬浮一个跑马灯的需求:
- 功能实现:
// 聊天页面
Stack(
alignment: Alignment.bottomCenter,
children: [
ListView.builder(...),
/// 跑马灯
MMarqueeWidget(),
],
)
参考徐医生的dojo
// 跑马灯组件实现,
import 'package:flutter/material.dart';
class MMarqueeWidget extends StatefulWidget {
final Widget child;
MMarqueeWidget({Key key, this.child}) : super(key: key);
@override
_MMarqueeWidgetState createState() => _MMarqueeWidgetState();
}
class _MMarqueeWidgetState extends State with SingleTickerProviderStateMixin {
AnimationController controller;
Animation animation;
@override
void initState() {
super.initState();
controller = AnimationController(vsync: this, duration: Duration(seconds: 10));
animation = Tween(begin: Offset(1, 0), end: Offset(-1, 0)).animate(controller);
controller.repeat();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
builder: (context, _) {
return ClipRect(
child: FractionalTranslation(
translation: animation.value,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: widget.child,
),
),
);
},
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
- 开启 DevTools 的 Repaint RainBow
发现跑马灯动画会导致其他部分也重绘
- RepaintBoundary优化
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: AnimatedBuilder(
animation: animation,
...
),
);
}
flutter提供了创建一个单独的图层,不会影响其他图层,参考https://www.bilibili.com/video/BV1F4411D7rP的24’20’’或者唯鹿的这篇https://www.jianshu.com/p/99d8a42f6704
❤️未完待续...