flutterdartflutter-custompainter

why are my brush strokes not showing up on the canvas


Im trying to design a simple drawing app in flutter and I'm having trouble trying to implement the actual brush strokes and how they look. Here is my code:

import 'package:flutter/material.dart';
import 'components/paint_selector.dart';
import 'components/paint_list.dart' as paintList;
import 'components/user_canvas.dart';
import 'components/objects/userLine.dart';


//boilderplate code
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // GlobalKey _key = GlobalKey();
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AGG Demo',
      // theme: ThemeData(
      //  primarySwatch: Colors.blue,
      // ),
      home: MyHomePage(),
    );
  }
}


class MyHomePage extends StatelessWidget {
  Paint globalBrush = Paint()
    ..color = Colors.black
    ..strokeWidth = 5;
    
  UserLine strokePath = UserLine();

  @override
  Widget build(BuildContext context) {
    List<Paint> paintList = [
      Paint()
        ..color = Colors.red,
      Paint()
        ..color = Colors.green,
      Paint()
        ..color = Colors.blue,
    ];
    double screenHeight = MediaQuery.of(context).size.height;
    double screenWidth = MediaQuery.of(context).size.width;
    return Container(
      height: screenHeight,
      width: screenWidth,
      color: Colors.black,
      child: GestureDetector(
        onPanStart: (details) {
          strokePath = UserLine();
          strokePath.points.add(details.localPosition);
        },
        onPanUpdate: (details) {
          strokePath.points.add(details.localPosition);
          print("localPosition = ${details.localPosition}");
          print("globalPosition = ${details.globalPosition}");
        },
        onPanEnd: (details) {
          //dont think we need to do anthing here yet!
        },
        child: CustomPaint(
          
          painter: UserCanvas(globalBrush, strokePath),
          child: PaintSelector(
            globalBrush: globalBrush,
            paintList: paintList,
          ),
        ),
      ),
    );
  }
}

Im not sure if my brushstrokes are working, but they're just invisible or if I'm messing up somewhere along the way. It might have something to do with another one of my widgets covering the canvas, because when I change the color of that one, the whole screen changes to the color of it aswell. Code:

import 'package:flutter/material.dart';


class PaintSelector extends StatefulWidget {
  final Paint globalBrush; // the brush the canvas uses - is changed when the user selects a new color
  final List<Paint>? paintList; // the list of paints that the user can choose from

  const PaintSelector({Key? key, required this.globalBrush, this.paintList}) : super(key: key);

  @override
  State<PaintSelector> createState() => _PaintSelectorState();
}

class _PaintSelectorState extends State<PaintSelector> {
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 20,
      height: 20,
      alignment: Alignment.topRight,
      color: Colors.black,
      padding: const EdgeInsets.only(top: 40),
      child: Column(
        children: [
          for (var paint in widget.paintList!)
            colorButton(
              paint: paint,
              globalBrush: widget.globalBrush,
            ),
       ]
      ),
    );
  }
}

class colorButton extends StatefulWidget {
  final Paint paint; // the individual paint that can be customized by the user eventually
  Paint globalBrush; //the brush the canvas is uses - is changed when the user selects a new color
  colorButton({super.key, required this.paint, required this.globalBrush});

  @override
  State<colorButton> createState() => _colorButtonState();
}

class _colorButtonState extends State<colorButton> {
  bool isSelected = false;
  double borderWidth = 1;
  @override

  Widget build(BuildContext context) {
    Color color = widget.paint.color;
    return GestureDetector(
      onTap: () {
        setState(() {
          widget.globalBrush = widget.paint;
          print("selected color = $color");
        });
      },
      onTapDown: (details) {
        setState(() {
          borderWidth = 4;
        });
      },
      onTapUp: (details) {
        setState(() {
          borderWidth = 1;
        });
      },
      child: Container(
        height: 50,
        width: 50,
        margin: const EdgeInsets.all(10),
        decoration: BoxDecoration(
          shape: BoxShape.circle,
          color: color,
          border: Border.all(
            color: Colors.white,
            width: borderWidth,
          ),
        ),
      ),
    );
  }
}

Solution

  • A couple of things.

    Some code change:

    Make MyHomePage a stateful widget and add the below:

    Paint globalBrush = Paint()..color = Colors.white..strokeWidth = 5;
      UserLine strokePath = UserLine();
    
      void updateGlobalBrush(Paint newBrush) {
        setState(() {
          globalBrush = newBrush;
        });
      }
    

    Most of the code remains same up until the child of the Container in MyHomePage.The child should be a stack as shown below:

    Stack(
            children: [
              CustomPaint(
                painter: UserCanvas(globalBrush, strokePath),
              ),
              PaintSelector(
                globalBrush: globalBrush,
                paintList: paintList,
                onBrushChanged: updateGlobalBrush,
              ),
            ],
          )
    

    We have added a callback for onBrushChanged to update the globalBrush. We modify PaintSelector to use this callback as shown below:

    class PaintSelector extends StatelessWidget {
      final Paint globalBrush;
      final List<Paint>? paintList;
      final Function(Paint) onBrushChanged;
    
      const PaintSelector({Key? key, required this.globalBrush, this.paintList, required this.onBrushChanged}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        // the existing code you have
        return Positioned(
          top: 40,
          right: 0,
          child: Column(
            children: [
              for (var paint in paintList!)
                colorButton(
                  paint: paint,
                  onSelected: onBrushChanged,
                ),
            ],
          ),
        );
      }
    }
    

    We also modify colorButton. its better you name it ColorButton(naming convention).

    class ColorButton extends StatelessWidget {
      final Paint paint;
      final Function(Paint) onSelected;
    
      ColorButton({Key? key, required this.paint, required this.onSelected}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        // the existing code you have
        return GestureDetector(
          onTap: () {
            onSelected(paint);
          },
          // the existing code you have
        );
      }
    }
    

    Hopefully this helps or provides you with some insight.