简体   繁体   English

更改 CustomPaint 的颜色更改所有以前的点

[英]Changing colour of CustomPaint changes for all previous points

So I'm trying to create a draw app using Flutter following the "signature canvas" method.所以我试图按照“签名画布”方法使用 Flutter 创建一个绘图应用程序。 However, I'm having trouble being able to change the colour of the CustomPaint object without it already changing the colours for each line draw before the change as shown here:但是,我无法更改 CustomPaint 对象的颜色,而在更改之前它没有更改每个线条绘制的颜色,如下所示: 在此处输入图片说明

As you can see, the colour change happens once the Page Widget's state is changed (either by clicking on the main FAB or if I were to draw on the canvas again).如您所见,一旦页面小部件的状态发生更改(通过单击主 FAB 或再次在画布上绘制),颜色就会发生变化。 Here is my code below for my DrawPage:下面是我的 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();
            }
          },
        ),
      ]),
    );
  }
}

What I've tried so far:到目前为止我尝试过的:

I've tried playing around with how the points are added to my List of Offsets as this list is recreated after each "draw" gesture such as just adding to the current List without recreating it but this breaks the "draw" gesture:我试过如何将点添加到我的偏移列表中,因为在每个“绘制”手势之后重新创建此列表,例如只是添加到当前列表而不重新创建它,但这会破坏“绘制”手势:

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

I've tried making a reference to the CustomPaint object or my Painter object outside of the build() scope and updating the color property that way but this also breaks the "draw" gesture.我已经尝试在 build() 范围之外引用 CustomPaint 对象或我的 Painter 对象并以这种方式更新颜色属性,但这也会破坏“绘制”手势。

Any help would be greatly appreciated!任何帮助将不胜感激!

Also, here's the code for my Painter class in case people wish to see it:另外,这是我的 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;
}

I think, for different colors you have to use different Paints.我认为,对于不同的颜色,您必须使用不同的油漆。 I've added small changes to your code, it works.我对您的代码进行了一些小的更改,它有效。

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

2020, I have a nice solution for this, because the actually chosen doesn't work for me and I see some redundant calls. 2020 年,我对此有一个很好的解决方案,因为实际选择的对我不起作用,而且我看到了一些冗余调用。

So, I start creating a small class:所以,我开始创建一个小类:

class _GroupPoints {
  Offset offset;
  Color color;
  _GroupPoints({this.offset, this.color});
}

next, i declare my CustomPainter like this:接下来,我像这样声明我的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;
}

And on my widget:在我的小部件上:

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

On this way we can use multiple draws of points with their respective color.通过这种方式,我们可以使用具有各自颜色的多个点绘制。 Hope this can help anybody.希望这可以帮助任何人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM