CustomPaint的更改颜色会更改所有先前的点

时间:2018-11-07 09:20:06

标签: android canvas colors dart flutter

因此,我尝试按照“签名画布”方法使用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对象,并以此方式更新color属性,但这也会破坏“绘制”手势。

任何帮助将不胜感激!

此外,如果有人希望看到以下代码,这是我的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;
}

2 个答案:

答案 0 :(得分:1)

我认为,对于不同的颜色,您必须使用不同的涂料。我已经对您的代码进行了一些小的更改,即可使用。

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;
}

答案 1 :(得分:0)

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,
                                      ),
                                    ),
                                  ),
                                ),
                              }

通过这种方式,我们可以使用具有各自颜色的多个点绘制。希望这可以帮助任何人。