Repository: flutter
Issue: Cannot guard a call to State.setState() from within State.dispose().
Labels: framework,d: api docs,has reproducible steps,P2,found in release: 3.3,found in release: 3.5,team-framework,triaged-framework

I have a method with a call to setState(). This method will be called from within State.dispose(), as well as from elsewhere within the lifespan of the State object. (it's a shutdown handler on an object, but I've abstracted that away in my simplified example below), but I cannot seem to guard the usage of setState() (with State.mounted) on the being-disposed-of State object. 

I'd like to make sure I don't call setState() when calling foo() from within dispose(). (But use setState() normally when calling from elsewhere). Guarding it with State.mounted is not working.

If I call the method before super.dispose()
```dart
    void foo() {
      if (this.mounted) setState((){...});
    }

    @override
    void dispose() {
      foo();
      super.dispose();
    }
```
Then I get this error, showing that setState() was called:

    The following assertion was thrown while finalizing the widget tree:
    flutter: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3473 pos 12:
    flutter: '_debugLifecycleState != _ElementLifecycle.defunct': is not true.
    flutter:
    flutter: Either the assertion indicates an error in the framework itself, or we should provide substantially
    flutter: more information in this error message to help you determine and fix the underlying cause.
    flutter: In either case, please report this assertion by filing a bug on GitHub:
    flutter:   https://github.com/flutter/flutter/issues/new?template=BUG.md
    flutter:
    flutter: When the exception was thrown, this was the stack:
    flutter: #2      Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:3473:12)
    flutter: #3      State.setState (package:flutter/src/widgets/framework.dart:1146:14)
    flutter: #4      _SubWidgetState.foo (package:experimental.sparklight.prototypes.audioplugintester/src/sub_widget.dart:44:9)
    flutter: #5      _SubWidgetState.dispose (package:experimental.sparklight.prototypes.audioplugintester/src/sub_widget.dart:52:5)
    flutter: #6      StatefulElement.unmount (package:flutter/src/widgets/framework.dart:3900:12)


I acknowledge that this makes some sense, as super.dispose() has not been run, so the superclass cleanup has not take place yet, so [mounted] is still true.

However, when I move the foo() to _after_ super.dispose():
```dart
    void foo() {
      if (this.mounted) setState((){...});
    }

    @override
    void dispose() {
      super.dispose();
      foo();
    }
```
I get a different message (but a similar-looking stack)

    [VERBOSE-2:shell.cc(184)] Dart Error: Unhandled exception:
    setState() called after dispose(): _SubWidgetState#68e4a(lifecycle state: defunct)
    This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback. The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
    This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().

     When the exception was thrown, this was the stack:
    flutter: #0      State.setState.<anonymous closure> (package:flutter/src/widgets/framework.dart:1103:9)
    flutter: #1      State.setState (package:flutter/src/widgets/framework.dart:1129:6)
    flutter: #2      _SubWidgetState.foo (package:experimental.sparklight.prototypes.audioplugintester/src/sub_widget.dart:44:9)
    flutter: #3      _SubWidgetState.dispose (package:experimental.sparklight.prototypes.audioplugintester/src/sub_widget.dart:55:5)
    flutter: #4      StatefulElement.unmount (package:flutter/src/widgets/framework.dart:3900:12)

showing that [mounted] is still true. This contradicts the comments in framework.dart for dispose():

  
    /// The framework calls this method when this [State] object will never
    /// build again. After the framework calls [dispose], the [State] object is
    /// considered unmounted and the [mounted] property is false. It is an error
    /// to call [setState] at this point. This stage of the lifecycle is terminal:
    /// there is no way to remount a [State] object that has been disposed.

and for setState()

    /// It is an error to call this method after the framework calls [dispose].
    /// You can determine whether it is legal to call this method by checking
    /// whether the [mounted] property is true.

  
Others have suggested adding my own ivar flag (i.e. _isDisposing, which is set to true at the start of my overridden State.dispose() and used as the guard in foo()) or multiple versions of foo() (i.e. foo() and fooWithoutSetState()). However, I'd prefer to not have to track the State lifecycle myself, nor add extra complexity in the number of methods. 
Thanks for looking!