mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add BuildContext.mounted (#111619)
This commit is contained in:
parent
fb28a0d3fe
commit
b8df71fcaf
@ -2081,6 +2081,29 @@ typedef ElementVisitor = void Function(Element element);
|
||||
///
|
||||
/// {@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]
|
||||
/// interface is used to discourage direct manipulation of [Element] objects.
|
||||
abstract class BuildContext {
|
||||
@ -2091,6 +2114,18 @@ abstract class BuildContext {
|
||||
/// managing the rendering pipeline for this context.
|
||||
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.
|
||||
///
|
||||
/// For [StatefulWidget]s and [StatelessWidget]s this flag is true while
|
||||
@ -3271,6 +3306,9 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
|
||||
Widget get widget => _widget!;
|
||||
Widget? _widget;
|
||||
|
||||
@override
|
||||
bool get mounted => _widget != null;
|
||||
|
||||
/// Returns true if the Element is defunct.
|
||||
///
|
||||
/// This getter always returns false in profile and release builds.
|
||||
|
||||
63
packages/flutter/test/widgets/build_context_test.dart
Normal file
63
packages/flutter/test/widgets/build_context_test.dart
Normal file
@ -0,0 +1,63 @@
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user