@@ -17,7 +17,7 @@ void main() {
1717 context,
1818 title: 'Test Form' ,
1919 submitLabel: 'Submit' ,
20- onSubmit: () async {},
20+ onSubmit: (_ ) async {},
2121 builder: (context, setState) => Column (
2222 children: [
2323 TextField (decoration: InputDecoration (hintText: 'Field 1' )),
@@ -55,7 +55,7 @@ void main() {
5555 context,
5656 title: 'Test Form' ,
5757 submitLabel: 'Save' ,
58- onSubmit: () async {},
58+ onSubmit: (_ ) async {},
5959 builder: (context, setState) => Column (
6060 children: [
6161 TextField (decoration: InputDecoration (hintText: 'Field 1' )),
@@ -111,9 +111,9 @@ void main() {
111111 title: 'Test Form' ,
112112 submitLabel: 'Go' ,
113113 canSubmit: () => true ,
114- onSubmit: () async {
114+ onSubmit: (sheetContext ) async {
115115 submitted = true ;
116- Navigator .pop (context );
116+ Navigator .pop (sheetContext );
117117 },
118118 builder: (context, setState) =>
119119 TextField (decoration: InputDecoration (hintText: 'Name' )),
@@ -132,4 +132,111 @@ void main() {
132132 expect (submitted, isTrue);
133133 });
134134 });
135+
136+ group ('onSubmit context lifetime' , () {
137+ testWidgets (
138+ 'sheet closes via onSubmit context even after the route that opened '
139+ 'it is already gone' ,
140+ (tester) async {
141+ // Reproduces the artist/album action-sheet flow:
142+ // 1. Action sheet's Edit row runs
143+ // Navigator.pop(actionSheetContext)
144+ // showEditDialog(actionSheetContext) // form sheet opens
145+ // 2. The form sheet captures the now-doomed actionSheetContext.
146+ // 3. Save tapped, awaits a network round-trip.
147+ // 4. By the time onSubmit's body runs, actionSheetContext is
148+ // defunct, so Navigator.pop on it silently fails.
149+ //
150+ // The fix: showFormSheet hands its own (always-mounted) context
151+ // to onSubmit, so the body uses that for pop/showOverlay.
152+
153+ await tester.pumpWidget (buildTestApp (
154+ child: Builder (
155+ builder: (rootContext) => ElevatedButton (
156+ onPressed: () {
157+ Navigator .of (rootContext).push (MaterialPageRoute <void >(
158+ builder: (innerContext) => Scaffold (
159+ body: Center (
160+ child: ElevatedButton (
161+ onPressed: () {
162+ Navigator .pop (innerContext);
163+ showFormSheet (
164+ innerContext,
165+ title: 'Edit' ,
166+ submitLabel: 'Save' ,
167+ canSubmit: () => true ,
168+ onSubmit: (sheetContext) async {
169+ // Simulate a network round-trip; the inner
170+ // route finishes its dismissal animation
171+ // during this gap.
172+ await Future <void >.delayed (
173+ const Duration (milliseconds: 50 ),
174+ );
175+ if (! sheetContext.mounted) return ;
176+ Navigator .pop (sheetContext);
177+ },
178+ builder: (_, __) => const SizedBox (),
179+ );
180+ },
181+ child: const Text ('Edit' ),
182+ ),
183+ ),
184+ ),
185+ ));
186+ },
187+ child: const Text ('Open' ),
188+ ),
189+ ),
190+ ));
191+
192+ await tester.tap (find.text ('Open' ));
193+ await tester.pumpAndSettle ();
194+
195+ await tester.tap (find.text ('Edit' ));
196+ await tester.pumpAndSettle ();
197+
198+ expect (find.text ('Save' ), findsOneWidget,
199+ reason: 'form sheet should be open' );
200+
201+ await tester.tap (find.text ('Save' ));
202+ await tester.pumpAndSettle ();
203+
204+ expect (find.text ('Save' ), findsNothing,
205+ reason: 'form sheet must close after Save' );
206+ },
207+ );
208+
209+ testWidgets ('onSubmit receives a mounted context' , (tester) async {
210+ BuildContext ? captured;
211+
212+ await tester.pumpWidget (buildTestApp (
213+ child: Builder (
214+ builder: (context) => ElevatedButton (
215+ onPressed: () => showFormSheet (
216+ context,
217+ title: 'Test' ,
218+ submitLabel: 'Save' ,
219+ canSubmit: () => true ,
220+ onSubmit: (sheetContext) async {
221+ captured = sheetContext;
222+ Navigator .pop (sheetContext);
223+ },
224+ builder: (_, __) => const SizedBox (),
225+ ),
226+ child: const Text ('Open' ),
227+ ),
228+ ),
229+ ));
230+
231+ await tester.tap (find.text ('Open' ));
232+ await tester.pumpAndSettle ();
233+ await tester.tap (find.text ('Save' ));
234+ await tester.pumpAndSettle ();
235+
236+ expect (captured, isNotNull);
237+ // After pop, the captured context should be unmounted — proving it
238+ // was the form sheet's own context, not some outer ancestor.
239+ expect (captured! .mounted, isFalse);
240+ });
241+ });
135242}
0 commit comments