Skip to content

Commit f614c59

Browse files
authored
Fix showDialog throws cryptic message when context is not active (#107323)
1 parent c58dca2 commit f614c59

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

packages/flutter/lib/src/material/dialog.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,7 @@ Future<T?> showDialog<T>({
11531153
assert(barrierDismissible != null);
11541154
assert(useSafeArea != null);
11551155
assert(useRootNavigator != null);
1156+
assert(_debugIsActive(context));
11561157
assert(debugCheckHasMaterialLocalizations(context));
11571158

11581159
final CapturedThemes themes = InheritedTheme.capture(
@@ -1176,6 +1177,23 @@ Future<T?> showDialog<T>({
11761177
));
11771178
}
11781179

1180+
bool _debugIsActive(BuildContext context) {
1181+
if (context is Element && !context.debugIsActive) {
1182+
throw FlutterError.fromParts(<DiagnosticsNode>[
1183+
ErrorSummary('This BuildContext is no longer valid.'),
1184+
ErrorDescription(
1185+
'The showDialog function context parameter is a BuildContext that is no longer valid.'
1186+
),
1187+
ErrorHint(
1188+
'This can commonly occur when the showDialog function is called after awaiting a Future. '
1189+
'In this situation the BuildContext might refer to a widget that has already been disposed during the await. '
1190+
'Consider using a parent context instead.',
1191+
),
1192+
]);
1193+
}
1194+
return true;
1195+
}
1196+
11791197
/// A dialog route with Material entrance and exit animations,
11801198
/// modal barrier color, and modal barrier behavior (dialog is dismissible
11811199
/// with a tap on the barrier).

packages/flutter/lib/src/widgets/framework.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3264,6 +3264,19 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
32643264
return isDefunct;
32653265
}
32663266

3267+
/// Returns true if the Element is active.
3268+
///
3269+
/// This getter always returns false in profile and release builds.
3270+
/// See the lifecycle documentation for [Element] for additional information.
3271+
bool get debugIsActive {
3272+
bool isActive = false;
3273+
assert(() {
3274+
isActive = _lifecycleState == _ElementLifecycle.active;
3275+
return true;
3276+
}());
3277+
return isActive;
3278+
}
3279+
32673280
/// The object that manages the lifecycle of this element.
32683281
@override
32693282
BuildOwner? get owner => _owner;

packages/flutter/test/material/dialog_test.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2122,6 +2122,41 @@ void main() {
21222122
expect(nestedObserver.dialogCount, 1);
21232123
});
21242124

2125+
testWidgets('showDialog throws a friendly user message when context is not active', (WidgetTester tester) async {
2126+
// Regression test for https://github.com/flutter/flutter/issues/12467
2127+
await tester.pumpWidget(
2128+
const MaterialApp(
2129+
home: Center(child: Text('Test')),
2130+
),
2131+
);
2132+
final BuildContext context = tester.element(find.text('Test'));
2133+
2134+
await tester.pumpWidget(
2135+
const MaterialApp(
2136+
home: Center(),
2137+
),
2138+
);
2139+
2140+
Object? error;
2141+
try {
2142+
showDialog<void>(
2143+
context: context,
2144+
builder: (BuildContext innerContext) {
2145+
return const AlertDialog(title: Text('Title'));
2146+
},
2147+
);
2148+
} catch(exception) {
2149+
error = exception;
2150+
}
2151+
2152+
expect(error, isNotNull);
2153+
expect(error, isFlutterError);
2154+
if (error is FlutterError) {
2155+
final ErrorSummary summary = error.diagnostics.first as ErrorSummary;
2156+
expect(summary.toString(), 'This BuildContext is no longer valid.');
2157+
}
2158+
});
2159+
21252160
group('showDialog avoids overlapping display features', () {
21262161
testWidgets('positioning with anchorPoint', (WidgetTester tester) async {
21272162
await tester.pumpWidget(

0 commit comments

Comments
 (0)