如何在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(), ), ), ), ], ), ), ), ); }, ); } }
问题原因
ClipRect会裁剪超出指定范围的绘制内容,平移超出CustomPaint固定尺寸后,网格就会被裁剪消失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
相关产品推荐
相关产品推荐

