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

Flutter垃圾管理APP:如何获取Custom Paint绘制的矩形位置(GetX)

垃圾管理APP矩形框选位置获取问题

我正在开发一款垃圾管理APP,用户拍摄图片后可在界面中用矩形框选垃圾区域。目前用Stack结合Custom Paint实现绘制功能,采用GetX状态管理,但无法获取绘制完成的矩形位置,而这些位置需要传入ML模型做后续处理。


界面代码

body: SizedBox(
        width: double.maxFinite,
        child: GetBuilder(builder: (DetectTrashController controller) {
          return Stack(
            children: [
              GestureDetector(
                onPanStart: (details) {
                  controller.startDrawing(details.localPosition);
                },
                onPanUpdate: (details) {
                  controller.updateDrawing(details.localPosition);
                },
                onPanEnd: (details) {
                  controller.endDrawing();
                },
                child: Stack(
                  children: [
                    Image.file(
                      fit: BoxFit.cover,
                      height: double.maxFinite,
                      width: double.maxFinite,
                      File(controller.imagePath!),
                    ),
                    SizedBox(
                      height: controller.image!.height.toDouble(),
                      width: controller.image!.width.toDouble(),
                      child: CustomPaint(
                        key: controller.widgetKey,
                        painter: RectanglePainter(
                          controller.rectangles,
                          controller.currentRect,
                          controller.image!,
                        ),
                        size: Size.infinite,
                      ),
                    ),
                    Padding(
                      padding: const EdgeInsets.all(8),
                      child: Align(
                        alignment: Alignment.topLeft,
                        child: InkWell(
                          onTap: () {
                            controller.toggleDrawing();
                          },
                          child: Container(
                              height: 31.v,
                              width: 161.h,
                              color: whiteColor,
                              child: Obx(
                                () => Row(
                                  mainAxisAlignment:
                                      MainAxisAlignment.spaceEvenly,
                                  children: [
                                    Checkbox(
                                      checkColor: whiteColor,
                                      fillColor: MaterialStatePropertyAll(
                                          controller.drawingEnabled.value
                                              ? Colors.black
                                              : null),
                                      shape: controller.drawingEnabled.value
                                          ? const CircleBorder()
                                          : null,
                                      value: controller.drawingEnabled.value,
                                      onChanged: (value) {
                                        controller.toggleDrawing();
                                      },
                                    ),
                                    Text(controller.drawingEnabled.value
                                        ? "Cancel Selection"
                                        : "Draw Selection"),
                                  ],
                                ),
                              )),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
              Padding(
                padding: EdgeInsets.only(bottom: 10.v),
                child: Align(
                  alignment: Alignment.bottomCenter,
                  child: CustomElevetedButton(
                    onPressed: () {},
                    backgroundColor: primaryColor,
                    child: "Update",
                    size: Size(189.h, 40.v),
                  ),
                ),
              )
            ],
          );
        }),
      ),

CustomPaint 类代码

class RectanglePainter extends CustomPainter {
  final List<Rectangle> rectangles;
  final Rect? currentRect;
  

  RectanglePainter(this.rectangles, this.currentRect,);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.red
      ..strokeWidth = 2
      ..style = PaintingStyle.stroke;
  

    // 绘制已完成的矩形
    for (var rectangle in rectangles) {
      canvas.drawRect(rectangle.rect, paint);
    }

    // 绘制正在拖动的当前矩形
    if (currentRect != null) {
      canvas.drawRect(currentRect!, paint);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true; // 每次都重绘以反映变化
  }
}

实现效果

矩形框选界面效果


解决方法

核心问题是你已经在Controller中维护了rectangles列表存储已完成的矩形,但需要正确暴露并转换这些坐标,适配ML模型需要的原始图片像素位置(注意界面图片拉伸的影响)。

  1. 确认Controller正确存储绘制完成的矩形
    检查DetectTrashController的endDrawing方法,确保已将绘制完成的currentRect添加到rectangles列表:

    void endDrawing() {
      if (currentRect != null && drawingEnabled.value) {
        rectangles.add(Rectangle(currentRect!)); // 假设Rectangle类持有Rect对象
        currentRect = null;
        update(); // 通知GetBuilder刷新界面
      }
    }
    
  2. 转换界面坐标为原始图片坐标
    界面中图片用BoxFit.cover拉伸显示,绘制的矩形是界面坐标,需要转换为原始图片的真实像素坐标:
    在Controller中添加转换方法:

    List<Rect> getOriginalImageRects() {
      final imageSize = Size(image!.width.toDouble(), image!.height.toDouble());
      final widgetSize = widgetKey.currentContext!.size!;
      
      // 计算图片在界面上的实际显示区域(适配BoxFit.cover的拉伸逻辑)
      final scale = max(widgetSize.width / imageSize.width, widgetSize.height / imageSize.height);
      final scaledImageSize = imageSize * scale;
      final dx = (widgetSize.width - scaledImageSize.width) / 2;
      final dy = (widgetSize.height - scaledImageSize.height) / 2;
    
      return rectangles.map((rect) {
        // 将界面坐标转换为原始图片坐标
        final adjustedRect = Rect.fromLTWH(
          (rect.rect.left - dx) / scale,
          (rect.rect.top - dy) / scale,
          rect.rect.width / scale,
          rect.rect.height / scale,
        );
        // 确保坐标不超出原始图片范围
        return adjustedRect.intersect(Rect.fromLTWH(0, 0, imageSize.width, imageSize.height));
      }).toList();
    }
    
  3. 获取坐标传入ML模型
    在"Update"按钮的点击事件中调用转换方法,拿到适配原始图片的矩形坐标:

    onPressed: () {
      final originalRects = controller.getOriginalImageRects();
      // 将originalRects传入ML模型处理,示例:
      // mlModel.processImageWithRects(controller.imagePath!, originalRects);
    },
    
  4. 验证坐标正确性
    可以打印转换后的坐标,对比原始图片的像素位置,确保矩形区域和用户框选的范围一致。


内容的提问来源于stack exchange,提问作者Muhmmad Nawaz Shah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 22:52:32