| 1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | import 'package:flutter/foundation.dart'; |
| 6 | |
| 7 | import 'framework.dart'; |
| 8 | |
| 9 | /// Provides non-leaking access to a [BuildContext]. |
| 10 | /// |
| 11 | /// A [BuildContext] is only valid if it is pointing to an active [Element]. |
| 12 | /// Once the [Element] is unmounted, the [BuildContext] should not be accessed |
| 13 | /// further. This class makes it possible for a [StatefulWidget] to share its |
| 14 | /// build context safely with other objects. |
| 15 | /// |
| 16 | /// Creators of this object must guarantee the following: |
| 17 | /// |
| 18 | /// 1. They create this object at or after [State.initState] but before |
| 19 | /// [State.dispose]. In particular, do not attempt to create this from the |
| 20 | /// constructor of a state. |
| 21 | /// 2. They call [dispose] from [State.dispose]. |
| 22 | /// |
| 23 | /// This object will not hold on to the [State] after disposal. |
| 24 | @optionalTypeArgs |
| 25 | class DisposableBuildContext<T extends State> { |
| 26 | /// Creates an object that provides access to a [BuildContext] without leaking |
| 27 | /// a [State]. |
| 28 | /// |
| 29 | /// Creators must call [dispose] when the [State] is disposed. |
| 30 | /// |
| 31 | /// [State.mounted] must be true. |
| 32 | DisposableBuildContext(T this._state) |
| 33 | : assert( |
| 34 | _state.mounted, |
| 35 | 'A DisposableBuildContext was given a BuildContext for an Element that is not mounted.' , |
| 36 | ) { |
| 37 | assert(debugMaybeDispatchCreated('widgets' , 'DisposableBuildContext' , this)); |
| 38 | } |
| 39 | |
| 40 | T? _state; |
| 41 | |
| 42 | /// Provides safe access to the build context. |
| 43 | /// |
| 44 | /// If [dispose] has been called, will return null. |
| 45 | /// |
| 46 | /// Otherwise, asserts the [_state] is still mounted and returns its context. |
| 47 | BuildContext? get context { |
| 48 | assert(_debugValidate()); |
| 49 | return _state?.context; |
| 50 | } |
| 51 | |
| 52 | /// Called from asserts or tests to determine whether this object is in a |
| 53 | /// valid state. |
| 54 | /// |
| 55 | /// Always returns true, but will assert if [dispose] has not been called |
| 56 | /// but the state this is tracking is unmounted. |
| 57 | bool _debugValidate() { |
| 58 | assert( |
| 59 | _state == null || _state!.mounted, |
| 60 | 'A DisposableBuildContext tried to access the BuildContext of a disposed ' |
| 61 | 'State object. This can happen when the creator of this ' |
| 62 | 'DisposableBuildContext fails to call dispose when it is disposed.' , |
| 63 | ); |
| 64 | return true; |
| 65 | } |
| 66 | |
| 67 | /// Marks the [BuildContext] as disposed. |
| 68 | /// |
| 69 | /// Creators of this object must call [dispose] when their [Element] is |
| 70 | /// unmounted, i.e. when [State.dispose] is called. |
| 71 | void dispose() { |
| 72 | assert(debugMaybeDispatchDisposed(this)); |
| 73 | _state = null; |
| 74 | } |
| 75 | } |
| 76 | |