更改CustomPaint的颜色会影响之前所有点的颜色。

4
我正在使用Flutter创建一个绘制应用程序,遵循“签名画布”方法。 然而,我有困难能够在不改变以前每条线的颜色的情况下更改CustomPaint对象的颜色,如下所示:enter image description here 可以看到,颜色更改发生在页面小部件的状态更改时(无论是通过单击主FAB还是再次在画布上绘制)。 下面是我的DrawPage代码:
class DrawPage extends StatefulWidget {
  @override
  DrawPageState createState() => new DrawPageState();
}

class DrawPageState extends State<DrawPage> with TickerProviderStateMixin {
  AnimationController controller;
  List<Offset> points = <Offset>[];
  Color color = Colors.black;
  StrokeCap strokeCap = StrokeCap.round;
  double strokeWidth = 5.0;

  @override
  void initState() {
    super.initState();
    controller = new AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 500),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: GestureDetector(
          onPanUpdate: (DragUpdateDetails details) {
            setState(() {
              RenderBox object = context.findRenderObject();
              Offset localPosition =
                  object.globalToLocal(details.globalPosition);
              points = new List.from(points);
              points.add(localPosition);
            });
          },
          onPanEnd: (DragEndDetails details) => points.add(null),
          child: CustomPaint(
            painter: Painter(
                points: points,
                color: color,
                strokeCap: strokeCap,
                strokeWidth: strokeWidth),
            size: Size.infinite,
          ),
        ),
      ),
      floatingActionButton:
          Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
        Container(
          height: 70.0,
          width: 56.0,
          alignment: FractionalOffset.topCenter,
          child: ScaleTransition(
            scale: CurvedAnimation(
              parent: controller,
              curve: Interval(0.0, 1.0 - 0 / 3 / 2.0, curve: Curves.easeOut),
            ),
            child: FloatingActionButton(
              mini: true,
              child: Icon(Icons.clear),
              onPressed: () {
                points.clear();
              },
            ),
          ),
        ),
        Container(
          height: 70.0,
          width: 56.0,
          alignment: FractionalOffset.topCenter,
          child: ScaleTransition(
            scale: CurvedAnimation(
              parent: controller,
              curve: Interval(0.0, 1.0 - 1 / 3 / 2.0, curve: Curves.easeOut),
            ),
            child: FloatingActionButton(
              mini: true,
              child: Icon(Icons.lens),
              onPressed: () {},
            ),
          ),
        ),
        Container(
            height: 70.0,
            width: 56.0,
            alignment: FractionalOffset.topCenter,
            child: ScaleTransition(
                scale: CurvedAnimation(
                  parent: controller,
                  curve:
                      Interval(0.0, 1.0 - 2 / 3 / 2.0, curve: Curves.easeOut),
                ),
                child: FloatingActionButton(
                    mini: true,
                    child: Icon(Icons.color_lens),
                    onPressed: () async {
                      Color temp;
                      temp = await showDialog(
                          context: context,
                          builder: (context) => ColorDialog());
                      if (temp != null) {
                        setState(() {
                          color = temp;
                        });
                      }
                    }))),
        FloatingActionButton(
          child: AnimatedBuilder(
            animation: controller,
            builder: (BuildContext context, Widget child) {
              return Transform(
                transform: Matrix4.rotationZ(controller.value * 0.5 * math.pi),
                alignment: FractionalOffset.center,
                child: Icon(Icons.brush),
              );
            },
          ),
          onPressed: () {
            if (controller.isDismissed) {
              controller.forward();
            } else {
              controller.reverse();
            }
          },
        ),
      ]),
    );
  }
}

我尝试过的方法:

我尝试改变将点添加到我的偏移列表中的方式,因为这个列表在每次“绘制”手势后都会重新创建,比如只是添加到当前列表而不重新创建,但是这会破坏“绘制”手势:

setState(() {
  RenderBox object = context.findRenderObject();
  Offset localPosition =
     object.globalToLocal(details.globalPosition);
  points = new List.from(points);
  points.add(localPosition);
});

我曾试图在build()作用域之外引用CustomPaint对象或Painter对象,并通过这种方式更新颜色属性,但这也会破坏“绘制”手势。

如果有帮助的话,将不胜感激!

此外,以下是我的Painter类的代码,以防有人想看:

class Painter extends CustomPainter {
  List<Offset> points;
  Color color;
  StrokeCap strokeCap;
  double strokeWidth;

  Painter({this.points, this.color, this.strokeCap, this.strokeWidth});

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = new Paint();
    paint.color = color;
    paint.strokeCap = strokeCap;
    paint.strokeWidth = strokeWidth;    

    for (int i = 0; i < points.length - 1; i++) {
      if (points[i] != null && points[i + 1] != null) {
        canvas.drawLine(points[i], points[i + 1], paint);
      }
    }
  }

  @override
  bool shouldRepaint(Painter oldPainter) => oldPainter.points != points;
}
3个回答

3

我认为,对于不同的颜色,你需要使用不同的绘画工具。我已经对你的代码进行了小的修改,现在它可以正常运行。

class DrawPageState extends State<DrawPage> with TickerProviderStateMixin {
  ...
  List<Painter> painterList = [];

  @override
  Widget build(BuildContext context) {
    ...
          child: CustomPaint(
            painter: Painter(
                points: points, color: color, strokeCap: strokeCap, strokeWidth: strokeWidth, painters: painterList),
            size: Size.infinite,
          ),
    ...
                onPressed: () async {
                  Color temp;
                  temp = await showDialog(
                      context: context,
                      builder: (context) => ColorDialog());
                  if (temp != null) {
                    setState(() {
                      painterList
                          .add(Painter(points: points.toList(), color: color, strokeCap: strokeCap, strokeWidth: strokeWidth));
                      points.clear();
                      strokeCap = StrokeCap.round;
                      strokeWidth = 5.0;
                      color = temp;
                    });
                  }
    ...
  }
}

class Painter extends CustomPainter {
  List<Offset> points;
  Color color;
  StrokeCap strokeCap;
  double strokeWidth;
  List<Painter> painters;

  Painter({this.points, this.color, this.strokeCap, this.strokeWidth, this.painters = const []});

  @override
  void paint(Canvas canvas, Size size) {
    for (Painter painter in painters) {
      painter.paint(canvas, size);
    }

    Paint paint = new Paint()
      ..color = color
      ..strokeCap = strokeCap
      ..strokeWidth = strokeWidth;
    for (int i = 0; i < points.length - 1; i++) {
      if (points[i] != null && points[i + 1] != null) {
        canvas.drawLine(points[i], points[i + 1], paint);
      }
    }
  }

  @override
  bool shouldRepaint(Painter oldDelegate) => oldDelegate.points != points;
}

太好了,我会试一下!刚升级到0.10.2版本,遇到了一些模拟器问题,但希望不会太长时间。 - soupjake

1
2020年,我有一个很好的解决方案,因为实际选择的方法对我不起作用,并且我看到一些冗余的调用。
因此,我开始创建一个小类:
class _GroupPoints {
  Offset offset;
  Color color;
  _GroupPoints({this.offset, this.color});
}

接下来,我像这样声明我的CustomPainter

class Signature extends CustomPainter {
  List<_GroupPoints> points;
  Color color;
  Signature({
    this.color,
    this.points,
  });

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = new Paint()
       // if you need this next params as dynamic, you can move it inside the for part
      ..strokeCap = StrokeCap.round
      ..strokeWidth = 5.0;

    for (int i = 0; i < newPoints.length - 1; i++) {
      paint.color = points[i].color;
      if (points[i].offset != null && points[i + 1].offset != null) {
        canvas.drawLine(points[i].offset, points[i + 1].offset, paint);
      }
      canvas.clipRect(Offset.zero & size);
    }
  }

  @override
  bool shouldRepaint(Signature oldDelegate) => true;
}

而且在我的小部件上:

...
class _MyPageState extends State<MyPage> {
  ...
  List<_GroupPoints> points = [];
  ...

                           Container(
                                  height: 500,
                                  width: double.infinity,
                                  child: GestureDetector(
                                    onPanUpdate: (DragUpdateDetails details) {
                                      setState(() {
                                        points = new List.from(points)
                                          ..add(
                                            new _GroupPoints(
                                              offset: details.localPosition,
                                              color: myDynamicColor,
                                            ),
                                          );
                                      });
                                    },
                                    onPanEnd: (DragEndDetails details) {
                                      points.add(
                                        _GroupPoints(
                                            color: myDynamicColor,
                                            offset: null),
                                      );
                                    },
                                    child: CustomPaint(
                                      painter: Signature(
                                        newPoints: points,
                                        color: myDynamicColor,
                                      ),
                                    ),
                                  ),
                                ),
                              }

在这种方式下,我们可以使用多个点的绘制以及它们各自对应的颜色。希望这能帮助任何人。

我刚试过了,它可以正常工作。唯一的问题是在画几条线之后,应用程序变得非常卡顿。我正在尝试优化它,但到目前为止还没有成功。 - Donki

0
根据Flutter的架构,整个绘图是一条单独的路径。
因此,当我们改变其他绘画的颜色时,整个绘图的颜色也会改变。
在每次绘画结束后使用"path.reset()"。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接