Unverified Commit b8df71fc authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Add BuildContext.mounted (#111619)

parent fb28a0d3
...@@ -2081,6 +2081,29 @@ typedef ElementVisitor = void Function(Element element); ...@@ -2081,6 +2081,29 @@ typedef ElementVisitor = void Function(Element element);
/// ///
/// {@youtube 560 315 https://www.youtube.com/watch?v=rIaaH87z1-g} /// {@youtube 560 315 https://www.youtube.com/watch?v=rIaaH87z1-g}
/// ///
/// Avoid storing instances of [BuildContext]s because they may become invalid
/// if the widget they are associated with is unmounted from the widget tree.
/// {@template flutter.widgets.BuildContext.asynchronous_gap}
/// If a [BuildContext] is used across an asynchronous gap (i.e. after performing
/// an asynchronous operation), consider checking [mounted] to determine whether
/// the context is still valid before interacting with it:
///
/// ```dart
/// @override
/// Widget build(BuildContext context) {
/// return OutlinedButton(
/// onPressed: () async {
/// await Future<void>.delayed(const Duration(seconds: 1));
/// if (context.mounted) {
/// Navigator.of(context).pop();
/// }
/// },
/// child: const Text('Delayed pop'),
/// );
/// }
/// ```
/// {@endtemplate}
///
/// [BuildContext] objects are actually [Element] objects. The [BuildContext] /// [BuildContext] objects are actually [Element] objects. The [BuildContext]
/// interface is used to discourage direct manipulation of [Element] objects. /// interface is used to discourage direct manipulation of [Element] objects.
abstract class BuildContext { abstract class BuildContext {
...@@ -2091,6 +2114,18 @@ abstract class BuildContext { ...@@ -2091,6 +2114,18 @@ abstract class BuildContext {
/// managing the rendering pipeline for this context. /// managing the rendering pipeline for this context.
BuildOwner? get owner; BuildOwner? get owner;
/// Whether the [Widget] this context is associated with is currently
/// mounted in the widget tree.
///
/// Accessing the properties of the [BuildContext] or calling any methods on
/// it is only valid while mounted is true. If mounted is false, assertions
/// will trigger.
///
/// Once unmounted, a given [BuildContext] will never become mounted again.
///
/// {@macro flutter.widgets.BuildContext.asynchronous_gap}
bool get mounted;
/// Whether the [widget] is currently updating the widget or render tree. /// Whether the [widget] is currently updating the widget or render tree.
/// ///
/// For [StatefulWidget]s and [StatelessWidget]s this flag is true while /// For [StatefulWidget]s and [StatelessWidget]s this flag is true while
...@@ -3271,6 +3306,9 @@ abstract class Element extends DiagnosticableTree implements BuildContext { ...@@ -3271,6 +3306,9 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
Widget get widget => _widget!; Widget get widget => _widget!;
Widget? _widget; Widget? _widget;
@override
bool get mounted => _widget != null;
/// Returns true if the Element is defunct. /// Returns true if the Element is defunct.
/// ///
/// This getter always returns false in profile and release builds. /// This getter always returns false in profile and release builds.
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('StatefulWidget BuildContext.mounted', (WidgetTester tester) async {
late BuildContext capturedContext;
await tester.pumpWidget(TestStatefulWidget(
onBuild: (BuildContext context) {
capturedContext = context;
}
));
expect(capturedContext.mounted, isTrue);
await tester.pumpWidget(Container());
expect(capturedContext.mounted, isFalse);
});
testWidgets('StatelessWidget BuildContext.mounted', (WidgetTester tester) async {
late BuildContext capturedContext;
await tester.pumpWidget(TestStatelessWidget(
onBuild: (BuildContext context) {
capturedContext = context;
}
));
expect(capturedContext.mounted, isTrue);
await tester.pumpWidget(Container());
expect(capturedContext.mounted, isFalse);
});
}
typedef BuildCallback = void Function(BuildContext);
class TestStatelessWidget extends StatelessWidget {
const TestStatelessWidget({super.key, required this.onBuild});
final BuildCallback onBuild;
@override
Widget build(BuildContext context) {
onBuild(context);
return Container();
}
}
class TestStatefulWidget extends StatefulWidget {
const TestStatefulWidget({super.key, required this.onBuild});
final BuildCallback onBuild;
@override
State<TestStatefulWidget> createState() => _TestStatefulWidgetState();
}
class _TestStatefulWidgetState extends State<TestStatefulWidget> {
@override
Widget build(BuildContext context) {
widget.onBuild(context);
return Container();
}
}
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