From 938343560e472c727b011c6cb359058fe54d234a Mon Sep 17 00:00:00 2001 From: Dara Adedeji Date: Wed, 11 Nov 2026 18:12:47 -0500 Subject: [PATCH] Keep the page's address when a capture runs A screenshot or video export renders the strategy in a hidden app shell. That shell was an app with its own navigator, and an app's navigator reports its route to the browser, so every capture rewrote the address to /. Reloading after opening the camera dialog then opened the library instead of the strategy. The shell now has no app navigator; a plain one gives the captured view its route and overlay and reports nothing. --- lib/screenshot/offscreen_capture.dart | 16 ++++++++-- test/offscreen_capture_route_test.dart | 43 ++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 test/offscreen_capture_route_test.dart diff --git a/lib/screenshot/offscreen_capture.dart b/lib/screenshot/offscreen_capture.dart index 61d1e4f44..b243174b3 100644 --- a/lib/screenshot/offscreen_capture.dart +++ b/lib/screenshot/offscreen_capture.dart @@ -77,14 +77,24 @@ Widget wrapForOffscreenCapture( return MaterialApp( theme: Theme.of(context), debugShowCheckedModeBanner: false, - home: child, - builder: (context, child) { + // No `home`: with one, the app builds its own navigator, and an + // app's navigator reports its route to the browser. Every capture + // then rewrote the address of the page to `/`, and a reload after + // a screenshot opened the library instead of the strategy. The + // navigator below gives the captured view the route and overlay it + // expects and reports nothing. + builder: (context, _) { // The captured view paints its own backdrop, or none for a // transparent screenshot, so the shell paints nothing under it. return Portal( child: ShadAppBuilder( backgroundColor: Colors.transparent, - child: child!, + child: Navigator( + onGenerateRoute: (settings) => MaterialPageRoute( + settings: settings, + builder: (context) => child, + ), + ), ), ); }, diff --git a/test/offscreen_capture_route_test.dart b/test/offscreen_capture_route_test.dart new file mode 100644 index 000000000..3aa2138db --- /dev/null +++ b/test/offscreen_capture_route_test.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:icarus/screenshot/offscreen_capture.dart'; + +void main() { + testWidgets('a capture does not tell the browser the page moved', + (tester) async { + // On web the address is where a reload reopens the editor. The capture + // shell used to be an app with its own navigator, which reported `/` as + // the page's route: after a screenshot, a reload opened the library. + final reported = []; + tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( + SystemChannels.navigation, + (call) async { + if (call.method == 'routeInformationUpdated') { + reported.add((call.arguments as Map)['uri'] as String); + } + return null; + }, + ); + addTearDown( + () => tester.binding.defaultBinaryMessenger + .setMockMethodCallHandler(SystemChannels.navigation, null), + ); + + late BuildContext captured; + await tester.pumpWidget(wrapForOffscreenCapture( + Builder(builder: (context) { + captured = context; + return const Text('captured view'); + }), + )); + await tester.pump(); + + expect(find.text('captured view'), findsOneWidget); + expect(reported, isEmpty); + // What the captured view still gets from the shell. + expect(Navigator.maybeOf(captured), isNotNull); + expect(Overlay.maybeOf(captured), isNotNull); + expect(ModalRoute.of(captured), isNotNull); + }); +}