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 shouldRepaint
return oldDelegate.config != config;
}
}
编写主类 SelfPaintPage:
class _SelfPaintPageState extends State<SelfPaintPage> {
//绘制所需参数
PaintConfig _paintConfig;
void initState() {
// TODO: implement initState
super.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 类型的 _paintConfig
painter: 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 initState
super.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;
//初始化,要实现自刷新,此处需调用super
SelfPainter({PaintConfig config}) : super(repaint: config) {
this.config = config;
//初始化画笔
_paint = Paint()
..color = Colors.red
..isAntiAlias = true;
}
void paint(Canvas canvas, Size size) {
// TODO: implement paint
canvas.drawCircle(config.circleOffset, 30, _paint);
}
//当config 配置发生变化的时候触发重绘
bool shouldRepaint(covariant SelfPainter oldDelegate) {
// TODO: implement shouldRepaint
return 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();
}
}
评论