A stateful widget is defined as any widget which changes its state within its lifetime. But it is a very common practice for a StatelessWidget
to have a S
StatefulWidget vs StatelessWidget.
StatelessWidget -- A widget that does not require mutable state.
A stateless widget is a widget that describes part of the user interface by building a constellation of other widgets that describe the user interface more concretely. The building process continues recursively until the description of the user interface is fully concrete (e.g., consists entirely of RenderObjectWidgets, which describe concrete RenderObjects).
The
stateless
widget is useful when the part of the user interface you are describing does not depend on anything other than the configuration information in the object itself and the BuildContext in which the widget is inflated. For compositions that can change dynamically, e.g. due to having an internal clock-driven state, or depending on some system state, consider usingStatefulWidget
.
class GreenFrog extends StatelessWidget {
const GreenFrog({ Key key }) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(color: const Color(0xFF2DBD3A));
}
}
StatefulWidget -- A widget that has mutable state.
When Flutter builds a StatefulWidget
, it creates a State object. This object is where all the mutable state for that widget is held.
The concept of state is defined by two things:
1) The data used by the widget might change.
2) The data can't be read synchronously when the widget is built. (All state must be established by the time the build method is called).
StatefulWidget lifecycle
The lifecycle has the following simplified steps:
createState()
.
Creates the mutable state for this widget at a given location in the tree.
Subclasses should override this method to return a newly created instance of their associated State subclass:
@override
_MyState createState() => _MyState();
this.mounted
property. It turns true when the buildContext
is assigned. It is an error to call setState
when a widget is unmounted.
Whether this State object is currently in a tree.
After creating a State object and before calling
initState
, the framework "mounts" the State object by associating it with a
BuildContext
. The State object remains mounted until the framework
callsdispose()
, after which time the framework will never ask the
State object to build again.It is an error to call setState unless mounted is true.
bool get mounted => _element != null;
initState
is called once and only once. It must call super.initState().
Initialize data that relies on the specific BuildContext for the created instance of the widget.
Initialize properties that rely on these widgets ‘parent’ in the tree.
Subscribe to Streams,
ChangeNotifiers
, or any other object that could change the data on this widget.
@override
initState() {
super.initState();
// Add listeners to this class
cartItemStream.listen((data) {
_updateWidget(data);
});
}
This method is also called immediately after
initState
. It is safe to callBuildContext.inheritFromWidgetOfExactType
from this method.Subclasses rarely override this method because the framework always calls build after dependency changes. Some subclasses do override this method because they need to do some expensive work (e.g., network fetches) when their dependencies change, and that work would be too expensive to do for every build.
@protected
@mustCallSuper
void didChangeDependencies() { }
The framework calls this method in a number of different situations:
initState
.didUpdateWidget
.setState
.
The framework replaces the subtree below this widget with the widget returned by this method, either by updating the existing subtree or by removing the subtree and inflating a new subtree, depending on whether the widget returned by this method can update the root of the existing subtree, as determined by calling
Widget.canUpdate
.Typically implementations return a newly created constellation of widgets that are configured with information from this widget's constructor, the given BuildContext, and the internal state of this State object.
@override
Widget build(BuildContext context, MyButtonState state) {
... () { print("color: $color"); } ...
}
If the parent widget rebuilds and request that this location in the tree update to display a new widget with the same runtime type and Widget.key, the framework will update the widget property of this State object to refer to the new widget and then call this method with the previous widget as an argument.
Override this method to respond when the widget changes (e.g., to start implicit animations).
The framework always calls build after calling didUpdateWidget, which means any calls to setState in didUpdateWidget are redundant.
@mustCallSuper
@protected
void didUpdateWidget(covariant T oldWidget) { }
setState
:
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.If you just change the state directly without calling setState, the framework might not schedule a build and the user interface for this subtree might not be updated to reflect the new state.
setState(() { _myState = newValue });
- The framework calls this method whenever it removes this State object from the tree. In some cases, the framework will reinsert the State object into another part of the tree (e.g., if the subtree containing this State object is grafted from one location in the tree to another). If that happens, the framework will ensure that it calls build to give the State object a chance to adapt to its new location in the tree. If the framework does reinsert this subtree, it will do so before the end of the animation frame in which the subtree was removed from the tree. For this reason, State objects can defer releasing most resources until the framework calls their dispose method.
This is rarely used.
@protected
@mustCallSuper
void deactivate() { }
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 of.Subclasses should override this method to release any resources retained by this object (e.g., stop any active animations).
@protected
@mustCallSuper
void dispose() {
assert(_debugLifecycleState == _StateLifecycle.ready);
assert(() { _debugLifecycleState = _StateLifecycle.defunct; return true; }());
}
For more info go here here, here
As mention in flutter docs
What’s the point?
Some widgets are stateful, and some are stateless. If a widget changes—the user interacts with it, for example—it’s stateful. A widget’s state consists of values that can change, like a slider’s current value or whether a checkbox is checked. A widget’s state is stored in a State object, separating the widget’s state from its appearance. When the widget’s state changes, the state object calls setState(), telling the framework to redraw the widget.
A stateless widget has no internal state to manage. Icon, IconButton, and Text are examples of stateless widgets, which subclass StatelessWidget.
A stateful widget is dynamic. The user can interact with a stateful widget (by typing into a form, or moving a slider, for example), or it changes over time (perhaps a data feed causes the UI to update). Checkbox, Radio, Slider, InkWell, Form, and TextField are examples of stateful widgets, which subclass StatefulWidget.
https://flutter.io/tutorials/interactive/#stateful-stateless