transparent_tap 0.1.0
transparent_tap: ^0.1.0 copied to clipboard
A Flutter widget that lets taps pass through the transparent pixels of an image, so only the visible (opaque) part of it is tappable.
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:transparent_tap/transparent_tap.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: DemoPage());
}
}
class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
ui.Image? _ringImage;
int _behindTaps = 0;
int _ringTaps = 0;
@override
void initState() {
super.initState();
_generateRingImage();
}
// Draws a solid ring with a fully transparent center and transparent
// corners, so we can demonstrate click-through without bundling an asset.
Future<void> _generateRingImage() async {
const size = 240.0;
final recorder = ui.PictureRecorder();
final canvas = Canvas(recorder);
final paint = Paint()..color = Colors.deepPurple;
canvas.drawCircle(const Offset(size / 2, size / 2), size / 2, paint);
// Punch a transparent hole out of the middle.
final clearPaint = Paint()..blendMode = BlendMode.clear;
canvas.drawCircle(const Offset(size / 2, size / 2), size / 4, clearPaint);
final picture = recorder.endRecording();
final image = await picture.toImage(size.toInt(), size.toInt());
setState(() => _ringImage = image);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('transparent_tap demo')),
body: Center(
child: Stack(
alignment: Alignment.center,
children: [
// The "behind" layer — tapping the ring's transparent center
// or corners should reach this.
GestureDetector(
onTap: () => setState(() => _behindTaps++),
child: Container(
width: 240,
height: 240,
color: Colors.amber.shade100,
alignment: Alignment.center,
child: Text('behind taps: $_behindTaps'),
),
),
// The ring overlay — only the solid purple band is tappable.
if (_ringImage != null)
TransparentTap(
image: _ringImage,
child: GestureDetector(
onTap: () => setState(() => _ringTaps++),
child: SizedBox(
width: 240,
height: 240,
child: CustomPaint(
painter: _ImagePainter(_ringImage!),
),
),
),
),
Positioned(
bottom: 0,
child: Text('ring taps: $_ringTaps'),
),
],
),
),
);
}
}
class _ImagePainter extends CustomPainter {
_ImagePainter(this.image);
final ui.Image image;
@override
void paint(Canvas canvas, Size size) {
paintImage(
canvas: canvas,
rect: Offset.zero & size,
image: image,
fit: BoxFit.contain,
);
}
@override
bool shouldRepaint(covariant _ImagePainter oldDelegate) =>
oldDelegate.image != image;
}