dismiss the lightbox on a tap outside the image (T-309)

The image fills the 94% viewer with a transparent letterbox, so taps on
the dimmed canvas beside the image hit the viewer and did nothing — only
the 6% margin dismissed. Add onTapUp that dismisses when the tap lands
outside the actual painted-image rect (computed from the RenderImage +
applyBoxFit, transformed for any zoom/pan). Double-tap reset, scroll
zoom, pan, Esc, and the close button are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-10 15:18:00 +02:00
co-authored by Claude Opus 4.8
parent 9607124c2b
commit 597b2f7622
5 changed files with 76 additions and 0 deletions
+26
View File
@@ -60,6 +60,32 @@ void main() {
expect(find.byType(InteractiveViewer), findsOneWidget);
});
testWidgets('a tap on the dimmed canvas dismisses; a tap on the image does not (T-309)', (tester) async {
final img = (await tester.runAsync(() => createTestImage(width: 100, height: 100)))!; // 1:1; real async
var dismissed = false;
await tester.pumpWidget(harness(
f,
MediaQuery(
data: const MediaQueryData(size: Size(800, 600)),
child: ClideLightbox(onDismiss: () => dismissed = true, child: RawImage(image: img, fit: BoxFit.contain)),
),
));
await tester.pumpAndSettle();
// The 94% box is ~752×564; a 1:1 image fits to 564×564 centred, leaving
// ~94px side margins. A tap in the left margin is dimmed canvas → dismiss.
final r = tester.getRect(find.byType(InteractiveViewer));
await tester.tapAt(Offset(r.left + 10, r.center.dy));
await tester.pump(const Duration(milliseconds: 350)); // past the double-tap delay
expect(dismissed, isTrue);
// A tap on the image itself does not dismiss.
dismissed = false;
await tester.tapAt(r.center);
await tester.pump(const Duration(milliseconds: 350));
expect(dismissed, isFalse);
});
testWidgets('scroll wheel zooms in and out', (tester) async {
await tester.pumpWidget(harness(f, box(() {})));
await tester.pumpAndSettle();