setState() called after dispose()
FlutterFlutter Problem Overview
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();
}
);
}
}
Flutter Solutions
Solution 1 - Flutter
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);
}
}
}
Solution 2 - Flutter
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);
});
}
Solution 3 - Flutter
Just write one line before setState()
if (!mounted) return;
and then
setState(() {
//Your code
});
Solution 4 - Flutter
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
}
Solution 5 - Flutter
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(() { }
Solution 6 - Flutter
Try this
Widget build(BuildContext context) {
return new RaisedButton(
child: new Text("${selectedDate.hour} ${selectedDate.minute}"),
onPressed: () async {
await initTimePicker();
}
);
}
Solution 7 - Flutter
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> {
....
}