setState method
Notify the framework that the internal state of this object has changed.
Whenever you change the internal state of aState object, make thechange in a function that you pass tosetState:
setState(() { _myState = newValue; });The provided callback is immediately called synchronously. It must notreturn a future (the callback cannot beasync), since then it would beunclear when the state was actually being set.
CallingsetState notifies the framework that the internal state of thisobject has changed in a way that might impact the user interface in thissubtree, which causes the framework to schedule abuild for thisStateobject.
If you just change the state directly without callingsetState, theframework might not schedule abuild and the user interface for thissubtree might not be updated to reflect the new state.
Generally it is recommended that thesetState method only be used towrap the actual changes to the state, not any computation that might beassociated with the change. For example, here a value used by thebuildfunction is incremented, and then the change is written to disk, but onlythe increment is wrapped in thesetState:
Future<void> _incrementCounter() async { setState(() { _counter++; }); Directory directory = await getApplicationDocumentsDirectory(); // from path_provider package final String dirName = directory.path; await File('$dirName/counter.txt').writeAsString('$_counter');}Sometimes, the changed state is in some other object not owned by thewidgetState, but the widget nonetheless needs to be updated to react tothe new state. This is especially common withListenables, such asAnimationControllers.
In such cases, it is good practice to leave a comment in the callbackpassed tosetState that explains what state changed:
void _update() { setState(() { /* The animation changed. */ });}//...animation.addListener(_update);It is an error to call this method after the framework callsdispose.You can determine whether it is legal to call this method by checkingwhether themounted property is true. That said, it is better practiceto cancel whatever work might trigger thesetState rather than merelychecking formounted before callingsetState, as otherwise CPU cycleswill be wasted.
Design discussion
The original version of this API was a method calledmarkNeedsBuild, forconsistency withRenderObject.markNeedsLayout,RenderObject.markNeedsPaint,et al.
However, early user testing of the Flutter framework revealed that peoplewould callmarkNeedsBuild() much more often than necessary. Essentially,people used it like a good luck charm, any time they weren't sure if theyneeded to call it, they would call it, just in case.
Naturally, this led to performance issues in applications.
When the API was changed to take a callback instead, this practice wasgreatly reduced. One hypothesis is that prompting developers to actuallyupdate their state in a callback caused developers to think more carefullyabout what exactly was being updated, and thus improved their understandingof the appropriate times to call the method.
In practice, thesetState method's implementation is trivial: it callsthe provided callback synchronously, then callsElement.markNeedsBuild.
Performance considerations
There is minimaldirect overhead to calling this function, and as it isexpected to be called at most once per frame, the overhead is irrelevantanyway. Nonetheless, it is best to avoid calling this function redundantly(e.g. in a tight loop), as it does involve creating a closure and callingit. The method is idempotent, there is no benefit to calling it more thanonce perState per frame.
Theindirect cost of causing this function, however, is high: it causesthe widget to rebuild, possibly triggering rebuilds for the entire subtreerooted at this widget, and further triggering a relayout and repaint ofthe entire correspondingRenderObject subtree.
For this reason, this method should only be called when thebuild methodwill, as a result of whatever state change was detected, change its resultmeaningfully.
See also:
- StatefulWidget, the API documentation for which has a section onperformance considerations that are relevant here.
Implementation
@protectedvoid setState(VoidCallback fn) { assert(() { if (_debugLifecycleState == _StateLifecycle.defunct) { throw FlutterError.fromParts(<DiagnosticsNode>[ ErrorSummary('setState() called after dispose(): $this'), ErrorDescription( '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.', ), ErrorHint( '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.', ), ErrorHint( '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().', ), ]); } if (_debugLifecycleState == _StateLifecycle.created && !mounted) { throw FlutterError.fromParts(<DiagnosticsNode>[ ErrorSummary('setState() called in constructor: $this'), ErrorHint( 'This happens when you call setState() on a State object for a widget that ' "hasn't been inserted into the widget tree yet. It is not necessary to call " 'setState() in the constructor, since the state is already assumed to be dirty ' 'when it is initially created.', ), ]); } return true; }()); final Object? result = fn() as dynamic; assert(() { if (result is Future) { throw FlutterError.fromParts(<DiagnosticsNode>[ ErrorSummary('setState() callback argument returned a Future.'), ErrorDescription( 'The setState() method on $this was called with a closure or method that ' 'returned a Future. Maybe it is marked as "async".', ), ErrorHint( 'Instead of performing asynchronous work inside a call to setState(), first ' 'execute the work (without updating the widget state), and then synchronously ' 'update the state inside a call to setState().', ), ]); } // We ignore other types of return values so that you can do things like: // setState(() => x = 3); return true; }()); _element!.markNeedsBuild();}