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,
),
),
),
);
}
}
A couple of things.
PaintSelector
on top of your CustomPaint
widgetcolorButton
widget, you are trying to change the globalBrush
directly. This will not reflect in the parent widget. Instead, you should use a callback function to notify the parent widget about the color change.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.