如何确保我的CustomPaint小部件绘画存储在栅格缓存中?

Mar*_*ark 11 flutter

我有一个应用程序,在用户触摸屏幕的位置显示一个黑点,如下所示:

在此输入图像描述

当用户将他的手指拖到屏幕上时,用户可以移动黑点.

背景是一个昂贵的绘制操作,所以我在堆栈中创建了两个单独的小部件,希望背景小部件绘画将存储在Flutter栅格缓存中.但它没有存储 - 每当黑点移动时,Flutter就会调用我昂贵的绘画方法.

我究竟做错了什么?

这是我的代码:

import 'package:flutter/material.dart';
import 'dart:math';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  State createState() => new MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  GlobalKey _paintKey = new GlobalKey();
  Offset _offset;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Stack(
        fit: StackFit.expand,
        children: <Widget>[
          new CustomPaint(
            painter: new ExpensivePainter(),
            isComplex: true,
            willChange: false,
          ),
          new Listener(
            onPointerDown: _updateOffset,
            onPointerMove: _updateOffset,
            child: new CustomPaint(
              key: _paintKey,
              painter: new MyCustomPainter(_offset),
              child: new ConstrainedBox(
                constraints: new BoxConstraints.expand(),
              ),
            ),
          )
        ],
      ),
    );
  }

  _updateOffset(PointerEvent event) {
    RenderBox referenceBox = _paintKey.currentContext.findRenderObject();
    Offset offset = referenceBox.globalToLocal(event.position);
    setState(() {
      _offset = offset;
    });
  }
}

class ExpensivePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    print("Doing expensive paint job");
    Random rand = new Random(12345);
    List<Color> colors = [
      Colors.red,
      Colors.blue,
      Colors.yellow,
      Colors.green,
      Colors.white,
    ];
    for (int i = 0; i < 5000; i++) {
      canvas.drawCircle(
          new Offset(
              rand.nextDouble() * size.width, rand.nextDouble() * size.height),
          10 + rand.nextDouble() * 20,
          new Paint()
            ..color = colors[rand.nextInt(colors.length)].withOpacity(0.2));
    }
  }

  @override
  bool shouldRepaint(ExpensivePainter other) => false;
}

class MyCustomPainter extends CustomPainter {
  final Offset _offset;

  MyCustomPainter(this._offset);

  @override
  void paint(Canvas canvas, Size size) {
    if (_offset == null) return;
    canvas.drawCircle(_offset, 10.0, new Paint()..color = Colors.black);
  }

  @override
  bool shouldRepaint(MyCustomPainter other) => other._offset != _offset;
}
Run Code Online (Sandbox Code Playgroud)

Rém*_*let 29

这是扑扑的特异性.我们没有做出反应,只有当他们的状态/道具改变时,"组件"才会被重新绘制.

在颤动中,每次小部件都必须重绘整个树.

通常,这不是问题而且相当快.但是有些情况(例如你的),你不想要那样.这是一个相当无证的小部件,但重要的出现!RepaintBoundary

有关Flutter渲染管道如何工作的优秀演讲:https://www.youtube.com/watch?v = UUfXWzp0-DU

但简而言之,可以考虑RepaintBoundary将绘画操作分成不同的部分.

无论如何,解决方案?将您的Expensive小部件包装在一个RepaintBoundary.突然你得到60 FPS.

      new RepaintBoundary(
        child: new CustomPaint(
          painter: new ExpensivePainter(),
          isComplex: true,
          willChange: false,
        ),
      ),
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述