80 lines
2.0 KiB
Dart
80 lines
2.0 KiB
Dart
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;
|
||
}
|
||
} |