Search code examples
flutterdartcanvas

Using multiple brush sizes for line thickness on Canvas - Flutter


I have created a Canvas using Flutter where we can draw lines using different brush sizes. There are three brushes with different sizes to set the thickness of the lines. However, changing the brush size affects the size of all previously drawn lines.

I would like to know how to prevent the thickness of previously drawn lines from changing when changing the brush size. Any suggestions or help would be appreciated.

Here is my code:

main.dart:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Canvas',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const CanvasPage(),
    );
  }
}

class CanvasPage extends StatefulWidget {
  const CanvasPage({super.key});

  @override
  CanvasPageState createState() => CanvasPageState();
}

class CanvasPageState extends State<CanvasPage> {
  List<Offset> _points = <Offset>[];
  double _pointSize = 5.0;

  void _handlePointerDown(PointerEvent event) {
    setState(() {
      _points = List.from(_points)..add(event.localPosition);
    });
  }

  void _handlePointerMove(PointerEvent event) {
    setState(() {
      _points = List.from(_points)..add(event.localPosition);
    });
  }

  void _handlePointerUp(PointerEvent event) {
    setState(() {
      _points = List.from(_points)..add(event.localPosition);
    });
  }

  void _changePointSize(double value) {
    setState(() {
      _pointSize = value;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Canvas'),
      ),
      body: Listener(
        onPointerDown: _handlePointerDown,
        onPointerMove: _handlePointerMove,
        onPointerUp: _handlePointerUp,
        child: CustomPaint(
          painter: CanvasPainter(_points, _pointSize),
          size: Size.infinite,
        ),
      ),
      bottomNavigationBar: BottomAppBar(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            IconButton(
              icon: const Icon(Icons.brush),
              onPressed: () {
                _changePointSize(5.0);
              },
            ),
            IconButton(
              icon: const Icon(Icons.brush),
              onPressed: () {
                _changePointSize(10.0);
              },
            ),
            IconButton(
              icon: const Icon(Icons.brush),
              onPressed: () {
                _changePointSize(15.0);
              },
            ),
          ],
        ),
      ),
    );
  }
}

class CanvasPainter extends CustomPainter {
  List<Offset> points;
  double pointSize;

  CanvasPainter(this.points, this.pointSize);

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.blue
      ..strokeCap = StrokeCap.round
      ..strokeWidth = pointSize;

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

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

Solution

  • Once you change the pointSize:

     void _changePointSize(double value) {
        setState(() {
          _pointSize = value;
        });
      }
    

    Your rebuilding the whole Custom Painter. I would suggest you try this(not sure it will work but):

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({Key? key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Canvas',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const CanvasPage(),
        );
      }
    }
    
    class CanvasPage extends StatefulWidget {
      const CanvasPage({Key? key});
    
      @override
      CanvasPageState createState() => CanvasPageState();
    }
    
    class CanvasPageState extends State<CanvasPage> {
      List<dynamic> _points = <dynamic>[];
      double _pointSize = 5.0;
    
      void _handlePointerDown(PointerEvent event) {
        setState(() {
          _points = List.from(_points)..add([event.localPosition,_pointSize]);
        });
      }
    
      void _handlePointerMove(PointerEvent event) {
        setState(() {
          _points = List.from(_points)..add([event.localPosition,_pointSize]);
        });
      }
    
      void _handlePointerUp(PointerEvent event) {
        setState(() {
          _points = List.from(_points)..add([event.localPosition,_pointSize]);
        });
      }
    
      void _changePointSize(double value) {
        setState(() {
          _pointSize = value;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Canvas'),
          ),
          body: Listener(
            onPointerDown: _handlePointerDown,
            onPointerMove: _handlePointerMove,
            onPointerUp: _handlePointerUp,
            child: CustomPaint(
              painter: CanvasPainter(_points),
              size: Size.infinite,
            ),
          ),
          bottomNavigationBar: BottomAppBar(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                IconButton(
                  icon: const Icon(Icons.brush),
                  onPressed: () {
                    _changePointSize(5.0);
                  },
                ),
                IconButton(
                  icon: const Icon(Icons.brush),
                  onPressed: () {
                    _changePointSize(10.0);
                  },
                ),
                IconButton(
                  icon: const Icon(Icons.brush),
                  onPressed: () {
                    _changePointSize(15.0);
                  },
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class CanvasPainter extends CustomPainter {
      List<dynamic> points;
    
      CanvasPainter(this.points);
    
      @override
      void paint(Canvas canvas, Size size) {
        Paint paint = Paint()
          ..color = Colors.blue
          ..strokeCap = StrokeCap.round;
    
        for (int i = 0; i < points.length - 1; i++) {
          paint.strokeWidth = points[i][1];
          canvas.drawLine(points[i][0], points[i + 1][0], paint);
        }
      }
    
      @override
      bool shouldRepaint(CanvasPainter oldDelegate) => true;
    }