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

Flutter Canvas多画笔尺寸绘制:如何避免修改画笔影响已有线条粗细

解决Flutter Canvas切换画笔尺寸不影响已绘制线条的问题

问题根源在于你当前只存储了绘制的点坐标,所有线条都使用同一个全局的画笔宽度重绘,切换尺寸后自然全部跟着变。要解决这个问题,需要把每一段独立的绘制路径(从按下到抬起的一笔)和对应的画笔尺寸绑定存储,让每一笔都保留自己的宽度。

以下是修改后的完整代码:

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 DrawingPath {
  final List<Offset> points;
  final double strokeWidth;

  DrawingPath(this.points, this.strokeWidth);
}

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

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

class CanvasPageState extends State<CanvasPage> {
  // 存储所有已完成的绘制路径
  List<DrawingPath> _drawingPaths = [];
  // 当前正在绘制的路径(未完成,手指还没抬起)
  DrawingPath? _currentPath;
  // 当前选中的画笔尺寸
  double _currentStrokeWidth = 5.0;

  void _handlePointerDown(PointerEvent event) {
    setState(() {
      // 按下时创建新的当前路径,使用当前选中的画笔尺寸
      _currentPath = DrawingPath([event.localPosition], _currentStrokeWidth);
    });
  }

  void _handlePointerMove(PointerEvent event) {
    if (_currentPath != null) {
      setState(() {
        // 移动时往当前路径添加新的点
        _currentPath!.points.add(event.localPosition);
      });
    }
  }

  void _handlePointerUp(PointerEvent event) {
    if (_currentPath != null) {
      setState(() {
        // 抬起时把当前路径加入已完成列表,清空当前路径
        _drawingPaths.add(_currentPath!);
        _currentPath = null;
      });
    }
  }

  void _changeStrokeWidth(double value) {
    setState(() {
      _currentStrokeWidth = 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(_drawingPaths, _currentPath),
          size: Size.infinite,
        ),
      ),
      bottomNavigationBar: BottomAppBar(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            IconButton(
              icon: const Icon(Icons.brush),
              onPressed: () => _changeStrokeWidth(5.0),
            ),
            IconButton(
              icon: const Icon(Icons.brush),
              onPressed: () => _changeStrokeWidth(10.0),
            ),
            IconButton(
              icon: const Icon(Icons.brush),
              onPressed: () => _changeStrokeWidth(15.0),
            ),
          ],
        ),
      ),
    );
  }
}

class CanvasPainter extends CustomPainter {
  final List<DrawingPath> completedPaths;
  final DrawingPath? currentPath;

  CanvasPainter(this.completedPaths, this.currentPath);

  @override
  void paint(Canvas canvas, Size size) {
    // 先绘制所有已完成的路径,每个路径用自己的画笔宽度
    for (final path in completedPaths) {
      final paint = Paint()
        ..color = Colors.blue
        ..strokeCap = StrokeCap.round
        ..strokeWidth = path.strokeWidth;

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

    // 再绘制当前正在进行的路径(手指未抬起时)
    if (currentPath != null) {
      final paint = Paint()
        ..color = Colors.blue
        ..strokeCap = StrokeCap.round
        ..strokeWidth = currentPath!.strokeWidth;

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

  @override
  bool shouldRepaint(CanvasPainter oldDelegate) {
    return oldDelegate.completedPaths != completedPaths || oldDelegate.currentPath != currentPath;
  }
}

关键改动说明:

  • 新增DrawingPath类:把每一笔的点集合和对应的画笔宽度绑定,确保每一笔都有自己的独立配置。
  • 拆分存储结构:用_drawingPaths存储所有已完成的绘制,_currentPath记录正在画的内容,避免全局变量影响所有线条。
  • 修改指针事件逻辑:按下时初始化带当前宽度的路径,移动时追加点,抬起时把路径存入完成列表。
  • 重写画笔绘制逻辑:遍历每个DrawingPath,用各自的strokeWidth创建画笔,确保每一笔用自己的宽度绘制。

内容的提问来源于stack exchange,提问作者ocean

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 02:42:04