Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setState() called after dispose()

Tags:

flutter

When I click the raised button, the timepicker is showing up. Now, if I wait 5 seconds, for example, and then confirm the time, this error will occur: setState() called after dispose()

I literally see in the console how flutter is updating the parent widgets, but why? I don't do anything - I just wait 5 seconds?! The example below will work in a normal project, however in my project which is quite more complex it won't work because Flutter is updating the states while I am waiting... What am I doing wrong? Does anyone have a guess at what it could be that Flutter is updating randomly in my more complex project and not in a simple project?

[UPDATE] I took a second look at it and found out it is updating from the level on where my TabBar and TabBarView are. Could it have to do something with the "with TickerProviderStateMixin" which I need for the TabBarView? Could it be that it causes the app to refresh regularly and randomly?

 class DateTimeButton extends State<DateTimeButtonWidget> {
  DateTime selectedDate = new DateTime.now();

  Future initTimePicker() async {
    final TimeOfDay picked = await showTimePicker(
      context: context,
      initialTime: new TimeOfDay(hour: selectedDate.hour, minute: selectedDate.minute),
    );

    if (picked != null) {
      setState(() {
        selectedDate = new DateTime(selectedDate.year, selectedDate.month, selectedDate.day, picked.hour, picked.minute);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return new RaisedButton(
      child: new Text("${selectedDate.hour} ${selectedDate.minute}"),
      onPressed: () {
        initTimePicker();
      }
    );
  }
}
like image 383
GreenTigerEye Avatar asked Mar 17 '18 17:03

GreenTigerEye


People also ask

Does state object live after Dispose ()?

'setState() called after dispose()' - we all have seen this multiple times and it means that State object has been removed permanently from the element tree(more about it here) and its UI can not be drawn on the screen anymore.

How do I dispose of setState?

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.

What happens when the setState () method is called in flutter?

Calling setState notifies the framework that the internal state of this object has changed in a way that might impact the user interface in this subtree, which causes the framework to schedule a build for this State object.

Why do you need to call setState (() }) in a StatefulWidget?

setState is a way to dynamically change the UI. We call it inside the State Object class of the StatefulWidget. Calling setState marks the corresponding Widget dirty .


7 Answers

Just check boolean property mounted of the state class of your widget before calling setState().

if (this.mounted) {
  setState(() {
    // Your state change code goes here
  });
}

Or even more clean approach Override setState method in your StatelfulWidget class.

class DateTimeButton extends StatefulWidget {
  @override
  void setState(fn) {
    if(mounted) {
      super.setState(fn);
    }
  }
}
like image 141
Ganapat Avatar answered Oct 09 '22 08:10

Ganapat


If it is an expected behavior that the Future completes when the widget already got disposed you can use

if (mounted) {
  setState(() {
    selectedDate = new DateTime(selectedDate.year, selectedDate.month, selectedDate.day, picked.hour, picked.minute);
  });
}
like image 25
Günter Zöchbauer Avatar answered Oct 09 '22 07:10

Günter Zöchbauer


Just write one line before setState()

 if (!mounted) return;

and then

setState(() {
      //Your code
    });
like image 34
Rahul Mahadik Avatar answered Oct 09 '22 09:10

Rahul Mahadik


I had the same problem and i solved changing the super constructor call order on initState():

Wrong code:

@override
  void initState() {
    foo_bar(); // call setState();
    super.initState(); // then foo_bar()
  }

Right code:

@override
  void initState() {
    super.initState();
    foo_bar(); // first call super constructor then foo_bar that contains setState() call
  }
like image 24
teteArg Avatar answered Oct 09 '22 09:10

teteArg


To prevent the error from occurring, one can make use of the mounted property of the State class to ensure that a widget is mounted before settings its state:

// First Update data 

if (!mounted) { 
      return;
 }
setState(() { }
like image 28
DimarBorda Avatar answered Oct 09 '22 08:10

DimarBorda


Try this

Widget build(BuildContext context) {
    return new RaisedButton(
        child: new Text("${selectedDate.hour} ${selectedDate.minute}"),
        onPressed: () async {
            await initTimePicker();
        }
    );
}
like image 36
PrimeNexes Avatar answered Oct 09 '22 09:10

PrimeNexes


class MountedState<T extends StatefulWidget> extends State<T> {
  @override
  Widget build(BuildContext context) {
    return null;
  }

  @override
  void setState(VoidCallback fn) {
    if (mounted) {
      super.setState(fn);
    }
  }
}

Example

To prevent the error,Instead of using State use MountedState

class ExampleStatefulWidget extends StatefulWidget {
  const ExampleStatefulWidget({Key key}) : super(key: key);

  @override
  _ExampleStatefulWidgetState createState() => _ExampleStatefulWidgetState();
}

class _ExampleStatefulWidgetState extends MountedState<ExampleStatefulWidget> {
  ....
}
like image 42
karzan kamal Avatar answered Oct 09 '22 09:10

karzan kamal