You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题根源

  1. 无状态组件无法更新UI:MyHomePage是StatelessWidget,修改globalBrush和strokePath时不会触发重绘,导致笔触变化无法显示。
  2. Widget层级覆盖:CustomPaint的child是PaintSelector,PaintSelector的Container设置了全屏背景色,覆盖了Canvas的绘制内容。
  3. Paint对象传递错误:直接修改传入的globalBrush对象不会触发父组件状态更新,无法同步笔刷变化。
  4. 未保存历史笔触:仅维护当前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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:29:51