Flutter绘图应用笔刷笔触不显示问题排查求助
Flutter绘图应用笔触显示问题解决方案
问题描述
开发简单Flutter绘图应用时,笔刷笔触无法正常显示,且修改部分Widget颜色会导致整个屏幕变色,怀疑存在Widget层级覆盖或状态更新异常的问题。
原代码
主页面代码
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, ), ), ), ); } }
PaintSelector代码
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, ), ), ), ); } }
问题根源
- 无状态组件无法更新UI:MyHomePage是StatelessWidget,修改globalBrush和strokePath时不会触发重绘,导致笔触变化无法显示。
- Widget层级覆盖:CustomPaint的child是PaintSelector,PaintSelector的Container设置了全屏背景色,覆盖了Canvas的绘制内容。
- Paint对象传递错误:直接修改传入的globalBrush对象不会触发父组件状态更新,无法同步笔刷变化。
- 未保存历史笔触:仅维护当前strokePath,每次绘制新路径时旧路径会丢失。
修改后的关键代码
主页面(改为StatefulWidget,维护历史笔触)
class MyHomePage extends StatefulWidget { @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { late Paint globalBrush; late UserLine currentStroke; List<UserLine> allStrokes = []; @override void initState() { super.initState(); globalBrush = Paint() ..color = Colors.black ..strokeWidth = 5 ..strokeCap = StrokeCap.round; // 优化笔触圆润度 currentStroke = UserLine(); } void updateBrushColor(Color color) { setState(() { globalBrush = Paint() ..color = color ..strokeWidth = globalBrush.strokeWidth ..strokeCap = StrokeCap.round; }); } @override Widget build(BuildContext context) { List<Paint> paintList = [ Paint()..color = Colors.red, Paint()..color = Colors.green, Paint()..color = Colors.blue, ]; return Scaffold( backgroundColor: Colors.black, body: GestureDetector( onPanStart: (details) { setState(() { currentStroke = UserLine(); currentStroke.points.add(details.localPosition); }); }, onPanUpdate: (details) { setState(() { currentStroke.points.add(details.localPosition); }); }, onPanEnd: (details) { setState(() { allStrokes.add(currentStroke); }); }, child: Stack( children: [ // 全屏绘制Canvas CustomPaint( size: MediaQuery.of(context).size, painter: UserCanvas(globalBrush, [...allStrokes, currentStroke]), ), // 右上角颜色选择器,不覆盖Canvas Positioned( top: 40, right: 20, child: PaintSelector( onBrushChanged: updateBrushColor, paintList: paintList, ), ), ], ), ), ); } }
PaintSelector(改用回调传递颜色,移除全屏背景)
class PaintSelector extends StatelessWidget { final Function(Color) onBrushChanged; final List<Paint> paintList; const PaintSelector({Key? key, required this.onBrushChanged, required this.paintList}) : super(key: key); @override Widget build(BuildContext context) { return Column( children: [ for (var paint in paintList) ColorButton( paint: paint, onTap: () => onBrushChanged(paint.color), ), ], ); } } class ColorButton extends StatefulWidget { final Paint paint; final VoidCallback onTap; const ColorButton({super.key, required this.paint, required this.onTap}); @override State<ColorButton> createState() => _ColorButtonState(); } class _ColorButtonState extends State<ColorButton> { double borderWidth = 1; @override Widget build(BuildContext context) { Color color = widget.paint.color; return GestureDetector( onTap: widget.onTap, onTapDown: (_) => setState(() => borderWidth = 4), onTapUp: (_) => 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, ), ), ), ); } }
UserCanvas(支持绘制多路径)
class UserCanvas extends CustomPainter { final Paint brush; final List<UserLine> strokes; UserCanvas(this.brush, this.strokes); @override void paint(Canvas canvas, Size size) { for (var stroke in strokes) { if (stroke.points.length < 2) continue; Path path = Path(); path.moveTo(stroke.points[0].dx, stroke.points[0].dy); for (int i = 1; i < stroke.points.length; i++) { path.lineTo(stroke.points[i].dx, stroke.points[i].dy); } canvas.drawPath(path, brush); } } @override bool shouldRepaint(covariant UserCanvas oldDelegate) { return oldDelegate.brush != brush || oldDelegate.strokes != strokes; } }
核心优化点
- 将MyHomePage改为StatefulWidget,通过
setState触发UI更新。 - 使用Stack层级,让ColorSelector悬浮在Canvas上方,避免覆盖绘制区域。
- 用回调函数传递颜色选择事件,确保父组件同步更新笔刷状态。
- 维护历史笔触列表,保留所有绘制内容。
- 给Paint添加
strokeCap: StrokeCap.round,优化笔触显示效果。
内容的提问来源于stack exchange,提问作者HelloToxicity
相关产品推荐
相关产品推荐

