Unverified Commit f614c597 authored by Bruno Leroux's avatar Bruno Leroux Committed by GitHub

Fix showDialog throws cryptic message when context is not active (#107323)

parent c58dca2a
...@@ -1153,6 +1153,7 @@ Future<T?> showDialog<T>({ ...@@ -1153,6 +1153,7 @@ Future<T?> showDialog<T>({
assert(barrierDismissible != null); assert(barrierDismissible != null);
assert(useSafeArea != null); assert(useSafeArea != null);
assert(useRootNavigator != null); assert(useRootNavigator != null);
assert(_debugIsActive(context));
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
final CapturedThemes themes = InheritedTheme.capture( final CapturedThemes themes = InheritedTheme.capture(
...@@ -1176,6 +1177,23 @@ Future<T?> showDialog<T>({ ...@@ -1176,6 +1177,23 @@ Future<T?> showDialog<T>({
)); ));
} }
bool _debugIsActive(BuildContext context) {
if (context is Element && !context.debugIsActive) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('This BuildContext is no longer valid.'),
ErrorDescription(
'The showDialog function context parameter is a BuildContext that is no longer valid.'
),
ErrorHint(
'This can commonly occur when the showDialog function is called after awaiting a Future. '
'In this situation the BuildContext might refer to a widget that has already been disposed during the await. '
'Consider using a parent context instead.',
),
]);
}
return true;
}
/// A dialog route with Material entrance and exit animations, /// A dialog route with Material entrance and exit animations,
/// modal barrier color, and modal barrier behavior (dialog is dismissible /// modal barrier color, and modal barrier behavior (dialog is dismissible
/// with a tap on the barrier). /// with a tap on the barrier).
......
...@@ -3264,6 +3264,19 @@ abstract class Element extends DiagnosticableTree implements BuildContext { ...@@ -3264,6 +3264,19 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
return isDefunct; return isDefunct;
} }
/// Returns true if the Element is active.
///
/// This getter always returns false in profile and release builds.
/// See the lifecycle documentation for [Element] for additional information.
bool get debugIsActive {
bool isActive = false;
assert(() {
isActive = _lifecycleState == _ElementLifecycle.active;
return true;
}());
return isActive;
}
/// The object that manages the lifecycle of this element. /// The object that manages the lifecycle of this element.
@override @override
BuildOwner? get owner => _owner; BuildOwner? get owner => _owner;
......
...@@ -2122,6 +2122,41 @@ void main() { ...@@ -2122,6 +2122,41 @@ void main() {
expect(nestedObserver.dialogCount, 1); expect(nestedObserver.dialogCount, 1);
}); });
testWidgets('showDialog throws a friendly user message when context is not active', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/12467
await tester.pumpWidget(
const MaterialApp(
home: Center(child: Text('Test')),
),
);
final BuildContext context = tester.element(find.text('Test'));
await tester.pumpWidget(
const MaterialApp(
home: Center(),
),
);
Object? error;
try {
showDialog<void>(
context: context,
builder: (BuildContext innerContext) {
return const AlertDialog(title: Text('Title'));
},
);
} catch(exception) {
error = exception;
}
expect(error, isNotNull);
expect(error, isFlutterError);
if (error is FlutterError) {
final ErrorSummary summary = error.diagnostics.first as ErrorSummary;
expect(summary.toString(), 'This BuildContext is no longer valid.');
}
});
group('showDialog avoids overlapping display features', () { group('showDialog avoids overlapping display features', () {
testWidgets('positioning with anchorPoint', (WidgetTester tester) async { testWidgets('positioning with anchorPoint', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment