Flutter绘图功能插件canvas的使用

Flutter绘图功能插件canvas的使用

Canvas 是一个 Flutter 包,提供了类似于 Canva 的编辑工具包。

特性

  • ✅ 拖动和调整小部件大小
  • ✅ 旋转小部件
  • ✅ 翻转小部件
  • ✅ 分组小部件

注意事项

Canvas 目前仍在开发中,还有一些功能尚未实现:

  • ❌ 对象吸附
  • ❌ 对象重新父级化
  • ❌ 布局(类似于 Figma 中的自动布局)
  • ❌ 撤销/重做
  • ❌ 视口平移缩放

以下是一个完整的示例代码,展示如何在 Flutter 中使用 canvas 插件来实现基本的绘图功能。

示例代码

import 'dart:math';

import 'package:canvas/canvas.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      body: _ForceUpdate(
        child: CanvasExample(),
      ),
    ),
  ));
}

class _ForceUpdate extends StatelessWidget {
  final Widget child;

  const _ForceUpdate({super.key, required this.child});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return KeyedSubtree(
      key: UniqueKey(),
      child: child,
    );
  }
}

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

  [@override](/user/override)
  State<CanvasExample> createState() => _CanvasExampleState();
}

// 辅助函数:角度与弧度转换
double _degToRad(double deg) => deg * (pi / 180);
double _radToDeg(double rad) => rad * (180 / pi);

class _CanvasExampleState extends State<CanvasExample>
    with SingleTickerProviderStateMixin {
  late CanvasController _controller;

  [@override](/user/override)
  void initState() {
    super.initState();
    _controller = CanvasController(
      children: [
        // 第一个红色方块
        BoxCanvasItem(
            debugLabel: 'Red',
            decoration: GestureDetector(
              onTap: () {
                print('Red tapped');
              },
              child: Container(
                color: Colors.red,
                child: Center(
                  child: Text('Red'),
                ),
              ),
            ),
            layout: AbsoluteLayout(
              offset: Offset.zero,
              size: Size(100, 100),
              scale: Offset(2, 2),
              rotation: _degToRad(0),
            ),
            children: [
              // 红色方块内的绿色方块
              BoxCanvasItem(
                debugLabel: 'Green',
                decoration: GestureDetector(
                  onTap: () {
                    print('Green tapped');
                  },
                  child: Container(
                    color: Colors.green,
                    child: Center(
                      child: Text('Green'),
                    ),
                  ),
                ),
                children: [
                  // 绿色方块内的橙色方块
                  BoxCanvasItem(
                    debugLabel: 'Orange',
                    decoration: GestureDetector(
                      onTap: () {
                        print('Orange tapped');
                      },
                      child: Container(
                        color: Colors.orange,
                        child: Center(
                          child: Text('Orange'),
                        ),
                      ),
                    ),
                    layout: AbsoluteLayout(
                      offset: Offset(50, 50),
                      size: Size(50, 50),
                      rotation: _degToRad(25),
                    ),
                  ),
                ],
                layout: AbsoluteLayout(
                  offset: Offset(100, 100),
                  size: Size(50, 50),
                  rotation: _degToRad(44),
                ),
              ),
            ]),
        // 第二个紫色方块
        BoxCanvasItem(
          debugLabel: 'Purple',
          decoration: GestureDetector(
            onTap: () {
              print('Purple tapped');
            },
            child: Container(
              color: Colors.purple,
              child: Center(
                child: Text('Purple'),
              ),
            ),
          ),
          layout: AbsoluteLayout(
            offset: Offset(200, 0),
            size: Size(100, 100),
            scale: Offset(1, 1),
            rotation: _degToRad(25),
          ),
          children: [
            // 紫色方块内的黄色方块
            BoxCanvasItem(
              debugLabel: 'Yellow',
              decoration: GestureDetector(
                onTap: () {
                  print('Yellow tapped');
                },
                child: Container(
                  color: Colors.yellow,
                  child: Center(
                    child: Text('Yellow'),
                  ),
                ),
              ),
              children: [
                // 黄色方块内的蓝色方块
                BoxCanvasItem(
                  debugLabel: 'Blue',
                  decoration: GestureDetector(
                    onTap: () {
                      print('Blue tapped');
                    },
                    child: Container(
                      color: Colors.blue,
                      child: Center(
                        child: Text('Blue'),
                      ),
                    ),
                  ),
                  layout: AbsoluteLayout(
                    offset: Offset(50, 50),
                    size: Size(50, 50),
                  ),
                ),
              ],
              layout: AbsoluteLayout(
                offset: Offset(100, 100),
                size: Size(50, 50),
                scale: Offset(2, 2),
              ),
            ),
          ],
        ),
      ],
    );
  }

  bool _shiftDown = false; // 使用 Shift 键
  bool _altDown = false; // 使用 Alt 键
  bool _ctrlDown = false; // 使用 Ctrl 键
  FocusNode _focusNode = FocusNode();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Focus(
      focusNode: _focusNode,
      onKeyEvent: (node, event) {
        if (event.logicalKey == LogicalKeyboardKey.shiftLeft ||
            event.logicalKey == LogicalKeyboardKey.shiftRight) {
          if (event is KeyDownEvent) {
            setState(() {
              _shiftDown = true;
            });
          } else if (event is KeyUpEvent) {
            setState(() {
              _shiftDown = false;
            });
          }
          return KeyEventResult.handled;
        }
        if (event.logicalKey == LogicalKeyboardKey.altLeft ||
            event.logicalKey == LogicalKeyboardKey.altRight) {
          if (event is KeyDownEvent) {
            setState(() {
              _altDown = true;
            });
          } else if (event is KeyUpEvent) {
            setState(() {
              _altDown = false;
            });
          }
          return KeyEventResult.handled;
        }
        if (event.logicalKey == LogicalKeyboardKey.controlLeft ||
            event.logicalKey == LogicalKeyboardKey.controlRight) {
          if (event is KeyDownEvent) {
            setState(() {
              _ctrlDown = true;
            });
          } else if (event is KeyUpEvent) {
            setState(() {
              _ctrlDown = false;
            });
          }
          return KeyEventResult.handled;
        }
        return KeyEventResult.ignored;
      },
      child: Listener(
        onPointerDown: (event) {
          _focusNode.requestFocus();
        },
        child: CanvasViewport(
          controller: _controller,
          multiSelect: _shiftDown,
          resizeMode: ResizeMode.scale,
          proportionalResize: _altDown,
          symmetricResize: _ctrlDown,
          anchoredRotate: _altDown,
        ),
      ),
    );
  }
}

更多关于Flutter绘图功能插件canvas的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter绘图功能插件canvas的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,CustomPaintCanvas 是用于自定义绘图的强大工具。CustomPaint 是一个 widget,它允许你使用 Canvas 来绘制自定义图形、文本、图像等。Canvas 提供了丰富的 API 来绘制各种形状、路径、文本和图像。

基本使用步骤

  1. 创建 CustomPaint widget: CustomPaint 是一个 widget,它需要一个 painter 参数,该参数是一个 CustomPainter 对象。

  2. 实现 CustomPainter: CustomPainter 是一个抽象类,你需要实现 paintshouldRepaint 方法。paint 方法用于实际的绘图操作,shouldRepaint 方法用于决定是否需要重新绘制。

  3. 使用 Canvas 进行绘图: 在 paint 方法中,你可以使用 Canvas 对象来绘制各种图形。

示例代码

以下是一个简单的示例,展示如何使用 CustomPaintCanvas 来绘制一个矩形和圆形。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Flutter Canvas Example')),
        body: Center(
          child: CustomPaint(
            size: Size(300, 300),
            painter: MyPainter(),
          ),
        ),
      ),
    );
  }
}

class MyPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // 绘制一个矩形
    final rect = Rect.fromLTWH(50, 50, 200, 100);
    final paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;
    canvas.drawRect(rect, paint);

    // 绘制一个圆形
    final center = Offset(size.width / 2, size.height / 2);
    final radius = 50.0;
    final circlePaint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.fill;
    canvas.drawCircle(center, radius, circlePaint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}

解释

  1. CustomPaint:

    • size 参数指定了绘图区域的大小。
    • painter 参数指定了自定义的 CustomPainter 对象。
  2. MyPainter:

    • paint 方法中,我们使用 Canvas 对象来绘制一个矩形和一个圆形。
    • shouldRepaint 方法返回 false,表示不需要重新绘制。
  3. Canvas 绘图:

    • drawRect 方法用于绘制矩形。
    • drawCircle 方法用于绘制圆形。
    • Paint 对象用于设置绘图的颜色、样式等属性。

其他常用 Canvas 方法

  • drawLine: 绘制一条线。
  • drawPath: 绘制一个路径。
  • drawImage: 绘制一个图像。
  • drawText: 绘制文本(需要使用 TextPainter)。
  • drawArc: 绘制一个弧形。
  • drawOval: 绘制一个椭圆。

使用 TextPainter 绘制文本

void paint(Canvas canvas, Size size) {
  final textPainter = TextPainter(
    text: TextSpan(
      text: 'Hello, Flutter!',
      style: TextStyle(color: Colors.black, fontSize: 24),
    ),
    textDirection: TextDirection.ltr,
  );
  textPainter.layout();
  textPainter.paint(canvas, Offset(50, 150));
}
回到顶部