Flutter高效自刷新小技巧

共 5265字,需浏览 11分钟

 ·

2021-02-10 12:31

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 配置发生变化的时候触发重绘 @override bool shouldRepaint(covariant SelfPainter oldDelegate) { // TODO: implement shouldRepaint return oldDelegate.config != config; }}

编写主类 SelfPaintPage:

class _SelfPaintPageState extends State<SelfPaintPage> {  //绘制所需参数  PaintConfig _paintConfig;
@override void initState() { // TODO: implement initState super.initState(); //初始化为圆点为(200,200)处 _paintConfig = PaintConfig(Offset(200, 200) / 2); }
@override 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 { @override _SelfPaintPageState createState() => _SelfPaintPageState();}
class _SelfPaintPageState extends State<SelfPaintPage> { //绘制所需参数 PaintConfig _paintConfig;
@override void initState() { // TODO: implement initState super.initState(); //初始化为圆点为(200,200)处 _paintConfig = PaintConfig(Offset(200, 200) / 2); }
@override 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; }
@override void paint(Canvas canvas, Size size) { // TODO: implement paint
canvas.drawCircle(config.circleOffset, 30, _paint); }
//当config 配置发生变化的时候触发重绘 @override 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(); }}


浏览 116
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报