import 'package:flutter/material.dart'; import 'package:flutter/gestures.dart'; /// 遮罩层,icon 区域挖洞可交互 class HoleOverlay extends StatelessWidget { final Offset iconPosition; final double iconSize; final VoidCallback? onTap; const HoleOverlay({ super.key, required this.iconPosition, required this.iconSize, this.onTap, }); @override Widget build(BuildContext context) { return Listener( behavior: HitTestBehavior.translucent, onPointerDown: (PointerDownEvent event) { final RenderBox box = context.findRenderObject() as RenderBox; final Offset local = box.globalToLocal(event.position); final iconRect = Rect.fromLTWH( iconPosition.dx, iconPosition.dy, iconSize, iconSize, ); if (!iconRect.contains(local)) { // 遮罩区域,拦截并关闭弹窗 if (onTap != null) { onTap!(); } } // 如果点在icon区域,什么都不做,事件会传递到下层 }, child: CustomPaint( size: Size.infinite, painter: _HolePainter( iconPosition: iconPosition, iconSize: iconSize, ), ), ); } } class _HolePainter extends CustomPainter { final Offset iconPosition; final double iconSize; _HolePainter({required this.iconPosition, required this.iconSize}); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.white.withOpacity(0.0) ..blendMode = BlendMode.srcOver; // 画全屏遮罩 canvas.drawRect(Offset.zero & size, paint); // 挖洞 final holeRect = Rect.fromLTWH( iconPosition.dx, iconPosition.dy, iconSize, iconSize, ); final holePath = Path()..addOval(holeRect); paint.blendMode = BlendMode.clear; canvas.drawPath(holePath, paint); } @override bool shouldRepaint(covariant _HolePainter oldDelegate) { return iconPosition != oldDelegate.iconPosition || iconSize != oldDelegate.iconSize; } }