Flutter : Unhandled Exception: setState() called after dispose() occurs when refreshing the page

Viewed 6947

I am working on a card game and it includes 2 parts.

  1. InputPage
  2. GamePage

In InputPage() user picks cards and it has a new game button. But if he/she not like the cards , he/she can be able to restart game. There is a restart game button in InputPage() , when user click on it, page must be completely restart. I did this with Navigator.of method. But when user go to GamePage(), i got an error like this:

Unhandled Exception: setState() called after dispose(): _GamePageState#d4518(lifecycle state: defunct, not mounted)
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.

I refresh the page with Navigator.pushReplacement..

Do you have any better idea?

This is the code :

FlatButton(
          onPressed: () {
            Navigator.of(context).pushReplacement(new MaterialPageRoute(
                builder: (BuildContext context) => InputPage()));
          },
4 Answers

This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree

The solution is to check the mounted property of the state class of your widget before calling setState(), like this:

if (!mounted) return;

setState(){
  /** **/
 }

Or:

if (mounted) {
  setState(() {
    /** **/
  });
}

Instead of refreshing the entire Scaffold/page, I would recommend you to create a method that reset every state you have there because it's the same thing.

put this condition before onState()

if (!mounted)
return;

setState(){
    /** CODE HERE **/
}

Example Image

Use this example to understand how it works.

void main() => runApp(MaterialApp(home: DummyWidget()));

class DummyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) => HomePage();
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool _enabled = false; // For Switch
  double _value = 0; // For Slider

  // This is the trick!
  void _reset() {
    Navigator.pushReplacement(
      context,
      PageRouteBuilder(
        transitionDuration: Duration.zero,
        pageBuilder: (_, __, ___) => DummyWidget(),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Switch(
            value: _enabled,
            onChanged: (newValue) => setState(() => _enabled = newValue),
          ),
          Slider(
            value: _value,
            onChanged: (newValue) => setState(() => _value = newValue),
          ),
          FlatButton(
            onPressed: _reset,
            child: Text('RESET'),
          ),
        ],
      ),
    );
  }
}

If you want to use key then

void main() => runApp(MaterialApp(home: HomePage()));

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: KeyedSubtree(
        key: ValueKey<int>(_count),
        child: Column(
          children: [
            MainPage(),
            FlatButton(
              onPressed: () => setState(() => ++_count),
              child: Text('RESET'),
            )
          ],
        ),
      ),
    );
  }
}

class MainPage extends StatefulWidget {
  @override
  _MainPageState createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  bool _enabled = false; // For Switch
  double _value = 0; // For Slider.

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Switch(
          value: _enabled,
          onChanged: (newValue) => setState(() => _enabled = newValue),
        ),
        Slider(
          value: _value,
          onChanged: (newValue) => setState(() => _value = newValue),
        ),
      ],
    );
  }
}
Related