Flutter高效自刷新小技巧
Flutter 在继承CustomerPainter绘制界面时,很多情况都是通过setState来刷新绘制界面,但这种刷新方式存在弊端。在探索 CustomPainter 中高效正确的刷新方式,在
Flutter 绘制探索 1 | CustomPainter 正确刷新姿势 | 七日打卡
https://juejin.cn/post/6916297631366905864 
一文中详细的给出了答案。
讲解的也很详细。建议先移步学习一下,写的真的很棒。
这里要实现的是类似在 Android 绘制本身类中调用 postInvalidate()方法,触发绘制的实现方式。
在实现 Android 类似刷新方式之前,再看一下源码截图,再引用总结一下:
在 flutter 源码,custom_paint.dart 源文件中有这么几句

触发重绘的最高效方式是:1. 画笔继承 CustomerPainter 类,并在构造函数提供一个“repaint” 参数,当需要重绘时,该对象会进行通知它的监听者,从而进行重绘。PS:“repaint” 的参数是继承 Listenable 的类型参数,ChangeNotifier本身就是对 Listenable 继承实现,在继承CustomerPainter时,构造函数需调用super() 方法传入 “repaint” 实例。2. 继承 Listenable (比如通过 ChangeNotifier)并实现 CustomPainter,这样对象本身就可以直接提供通知。
以绘制一个跟随手指移动的小球为例,以通常的实现方式思路:
在手势控件 GestureDetector 的监听下,通过 setState 不断的控制改变绘制小球的圆心坐标参数即可实现,但 setState 刷新范围太广,不推荐使用。
按照之前说的高效绘制的思路,细细一想:
1. 我们在继承实现 CustomPainter 构造时需要调用 super(),传入一个 Listenable 监听实例对象,这样 CustomPainter 本身才能能根据监听重绘。
2. CustomPainter 中的paint绘制需要根据传入的参数进行绘制。
3. 那何不把绘制所需要的参数抽出一个参数类继承 Listenable 呢。
4. 我们还发现 ChangeNotifier 本身对 Listenable 进行了实现。
由此,我们可以抽出参数类如下:
//PaintConfig 参数类继承ChangeNotifier,这样在我们设置(改变参数的时候)// 就可以实现刷新class PaintConfig extends ChangeNotifier {//圆心,参数Offset circleOffset;//构造PaintConfig(Offset circle) {this.circleOffset = circle;}//设置触摸圆心,并调用notifyListeners通知监听者void setCircleOffset(Offset point) {this.circleOffset = point;notifyListeners();}}
编写绘制类 SelfPainter:
SelfPainter 类持有 PaintConfig 类型的参数绘制实例。
class SelfPainter extends CustomPainter {// 绘制所需参数PaintConfig config;//画笔Paint _paint;//初始化,要实现自刷新,此处需调用super()SelfPainter({PaintConfig config}) : super(repaint: config) {this.config = config;//初始化画笔}//...//当config 配置发生变化的时候触发重绘bool shouldRepaint(covariant SelfPainter oldDelegate) {// TODO: implement shouldRepaintreturn oldDelegate.config != config;}}
编写主类 SelfPaintPage:
class _SelfPaintPageState extends State<SelfPaintPage> {//绘制所需参数PaintConfig _paintConfig;void initState() {// TODO: implement initStatesuper.initState();//初始化为圆点为(200,200)处_paintConfig = PaintConfig(Offset(200, 200) / 2);}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("高效自刷新方式"),),body: GestureDetector(onPanDown: (detail) {//设置圆点,并通知_paintConfig.setCircleOffset(Offset(detail.localPosition.dx, detail.localPosition.dy));},onPanUpdate: (detail) {//设置圆点,并通知_paintConfig.setCircleOffset(Offset(detail.localPosition.dx, detail.localPosition.dy));},child: Container(width: double.infinity,height: double.infinity,color: Colors.blue,child: CustomPaint(//传入继承ChangeNotifier 类型的 _paintConfigpainter: SelfPainter(config: _paintConfig),),),),);}}
至此,类似 Android 绘制本身类中调用 postInvalidate()方法,触发绘制的实现方式完成。你学到了吗?还有最最重要的一点就是在绘制的界面超级复杂的时候,请千万别再用 setState 了。
效果图

完整代码
import 'dart:io';import 'dart:typed_data';import 'dart:ui';import 'package:file_picker/file_picker.dart';import 'package:flutter/material.dart';import 'package:flutter/services.dart';class SelfPaintPage extends StatefulWidget {_SelfPaintPageState createState() => _SelfPaintPageState();}class _SelfPaintPageState extends State<SelfPaintPage> {//绘制所需参数PaintConfig _paintConfig;void initState() {// TODO: implement initStatesuper.initState();//初始化为圆点为(200,200)处_paintConfig = PaintConfig(Offset(200, 200) / 2);}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("高效自刷新方式"),),body: GestureDetector(onPanDown: (detail) {_paintConfig.setCircleOffset(Offset(detail.localPosition.dx, detail.localPosition.dy));},onPanUpdate: (detail) {_paintConfig.setCircleOffset(Offset(detail.localPosition.dx, detail.localPosition.dy));},child: Container(width: double.infinity,height: double.infinity,color: Colors.blue,child: CustomPaint(painter: SelfPainter(config: _paintConfig),),),),);}}class SelfPainter extends CustomPainter {PaintConfig config;//画笔Paint _paint;//初始化,要实现自刷新,此处需调用superSelfPainter({PaintConfig config}) : super(repaint: config) {this.config = config;//初始化画笔_paint = Paint()..color = Colors.red..isAntiAlias = true;}void paint(Canvas canvas, Size size) {// TODO: implement paintcanvas.drawCircle(config.circleOffset, 30, _paint);}//当config 配置发生变化的时候触发重绘bool shouldRepaint(covariant SelfPainter oldDelegate) {// TODO: implement shouldRepaintreturn oldDelegate.config != config;}}//PaintConfig 参数类继承ChangeNotifier,这样在我们设置(改变参数的时候)// 就可以实现刷新class PaintConfig extends ChangeNotifier {//圆心Offset circleOffset;//构造PaintConfig(Offset circle) {this.circleOffset = circle;}//设置触摸圆心,并调用notifyListeners实现自绘制void setCircleOffset(Offset point) {this.circleOffset = point;notifyListeners();}}
评论
