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

如何在Flutter中用InteractiveViewer实现可交互无限网格(解决平移消失)

Flutter 无限交互网格平移消失问题解决方案

你在Flutter中尝试用InteractiveViewer结合CustomPainter实现可放置节点的无限交互网格,但平移操作会导致网格消失。现有代码如下:

/// Represents the diagram canvas widget.
class DiagramCanvas extends ConsumerWidget {
  const DiagramCanvas({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {

    return Consumer(
      builder: (context, watch, _) {
        final screenSize = MediaQuery.of(context).size;

        return Container(
          width: double.infinity,
          height: double.infinity,
          color: Colors.white, // Set your desired background color

          // Create an InteractiveViewer for panning and zooming
          child: InteractiveViewer(
            panEnabled: true,
            scaleEnabled: true,
            boundaryMargin: EdgeInsets.all(double.infinity),
            minScale: 0.5,
            maxScale: 2.0,
            child: SizedBox.fromSize(
              size: Size.infinite,
              child: Stack(
                clipBehavior: Clip.none,
                children: [
                  Positioned.fill(
                    child: ClipRect(
                      child: CustomPaint(
                        size: Size(screenSize.width * 2, screenSize.height * 2),
                        painter: DiagramGridPainter(),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      },
    );
  }
}

问题原因

  1. ClipRect会裁剪超出指定范围的绘制内容,平移超出CustomPaint固定尺寸后,网格就会被裁剪消失
  2. CustomPaint设置了固定的size(屏幕尺寸的2倍),无法覆盖无限平移的区域

修改方案

1. 调整网格绘制逻辑,实现无限延伸

修改DiagramGridPainter,根据画布的变换偏移计算绘制范围,确保平移时始终有网格覆盖视口及周边区域:

class DiagramGridPainter extends CustomPainter {
  final double gridSize = 20.0;
  final Color gridColor = Colors.grey.shade200;

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = gridColor
      ..strokeWidth = 1.0;

    // 获取画布的平移偏移量
    final transform = canvas.getTransform();
    final offsetX = -transform.getTranslation().x;
    final offsetY = -transform.getTranslation().y;

    // 计算网格线的起止范围,覆盖视口外的额外区域,避免平移时出现空白
    final startX = (offsetX ~/ gridSize) * gridSize - gridSize * 2;
    final endX = (offsetX + size.width) ~/ gridSize * gridSize + gridSize * 2;
    final startY = (offsetY ~/ gridSize) * gridSize - gridSize * 2;
    final endY = (offsetY + size.height) ~/ gridSize * gridSize + gridSize * 2;

    // 绘制垂直线
    for (double x = startX; x <= endX; x += gridSize) {
      canvas.drawLine(Offset(x, startY), Offset(x, endY), paint);
    }

    // 绘制水平线
    for (double y = startY; y <= endY; y += gridSize) {
      canvas.drawLine(Offset(startX, y), Offset(endX, y), paint);
    }
  }

  @override
  bool shouldRepaint(covariant DiagramGridPainter oldDelegate) {
    return oldDelegate.gridSize != gridSize || oldDelegate.gridColor != gridColor;
  }
}

2. 简化布局,移除裁剪限制

修改DiagramCanvas,去掉多余的嵌套和裁剪组件,让网格能无限延伸:

class DiagramCanvas extends ConsumerWidget {
  const DiagramCanvas({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Container(
      width: double.infinity,
      height: double.infinity,
      color: Colors.white,
      child: InteractiveViewer(
        panEnabled: true,
        scaleEnabled: true,
        boundaryMargin: EdgeInsets.all(double.infinity),
        minScale: 0.5,
        maxScale: 2.0,
        // 使用SizedBox.expand让CustomPaint充满整个可交互区域
        child: SizedBox.expand(
          child: CustomPaint(
            painter: DiagramGridPainter(),
          ),
        ),
      ),
    );
  }
}

修改说明

  • 移除ClipRect和多余的Stack、Positioned.fill,避免裁剪网格内容
  • 去掉重复的Consumer嵌套(ConsumerWidget本身已提供ref,无需额外嵌套)
  • CustomPaint不再设置固定size,而是通过SizedBox.expand适配父容器尺寸
  • 网格绘制逻辑根据画布实时偏移计算范围,确保平移、缩放时始终显示连续的网格

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 03:33:28