StatefulWidget vs StatelessWidget in Flutter

Jayesh Pansheriya
Analytics Vidhya
Published in
5 min readJan 11, 2020

Stateless Widgets

Stateless widgets are those in which you want to make a UI that does not need to be dynamically changed whenever you update any value bound to it. For example, if you want to make a button whose title doesn’t need to change dynamically, then you can create a separate widget for a button as a Stateless widget.

As the name suggests, Stateless widgets are immutable.

Stateless widgets remain the same even if the user interacts with them. This kind of widget has no state, so they can’t change according to an internal state. They can only react to higher widget changes.

Stateless widgets cannot change their state during the runtime of the app, which means the widgets cannot be redrawn while the app is in action.

Let us start with the traditional “Hello World” example −

import 'package:flutter/material.dart';void main() => runApp(MyHome());
class MyHome extends StatelessWidget {const MyHome({Key key}) : super(key: key);@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("Stateless Widget Demo",)),body: Center(child: Text("Hello World,"),), ), );} }

Stateful Widgets

Stateful widgets are just the reverse of Stateless widgets. This means when you want to make something that you want to change dynamically according to how a user interacts with it, then you can use the Stateful widget. For example, if you want to change the background color of the app on click of a button, you can make use of Stateful widget in this case.

A Stateful widget can contain another Stateless widget and vice-versa.

class MyHomePage extends StatefulWidget {@override_MyHomePageState createState() => _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("Stateless Widget Demo",)),body: Center(child: Text("Hello World,"),), ), );}

Stateful Widget Lifecycle

The lifecycle has the following simplified steps:

  • createState()
  • mounted == true
  • initState()
  • didChangeDependencies()
  • build()
  • didUpdateWidget()
  • setState()
  • deactivate()
  • dispose()
  • mounted == false

In single word: execution.

The tldr adaptation is that State objects are seemingly perpetual, yet StatefulWidgets (and all Widget subclasses) are discarded and reconstructed at whatever point setup changes. It’s extremely economical ie modest for Flutter to modify an impermanent gadget.

As State isn’t overwhelmed on each reconstruct, it keeps away from costly calculations, and gets at the states property, getters, setters and so forth everytime something is remade outline by outline.

Significant is this is the thing that permits Flutter activitys to exist. As State isn’t discarded, it can continually be revamping it’s Widget in light of information changes, and when required, assuming any.

createState()

At the point when Flutter is told to fabricate a StatefulWidget, it promptly calls createState(). This technique must exist. A StatefulWidget once in a while should be more confused than this.

class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => new _MyHomePageState();
}

mounted is true

When createState makes the state class, a buildContext is doled out to that state.

A BuildContext is, excessively disentangled, the spot in the gadget tree in which this widgets is set. Here’s a more drawn out clarification.

All widgets have a bool this.mounted property. It is turns genuine when the buildContext is appointed. It is a blunder to call setState when a widgets is unmounted.

tip: This property is valuable when a strategy on your state calls setState() yet it isn’t clear when or how regularly that technique will be called. Maybe its being brought in light of a stream refreshing. You can utilize if (mounted) {… to ensure the State exists before calling setState().

initState()

This is the first method called when the widget is created (after the class constructor, of course.)

initState is called once and only once. It must also call super.initState().

This @override method is the best time to:

Initialize data that relies on the specific BuildContext for the created instance of the widget.Initialize properties that rely on this 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);
});
}

didChangeDependencies()

The didChangeDependencies method is called immediately after initState on the first time the widget is built.

It will also be called whenever an object that this widget depends on data from is called. For example, if it relies on an InheritedWidget, which updates.

build is always called after didChangeDependencies is called, so this is rarely needed. However, this method is the first change you have to call BuildContext.inheritFromWidgetOfExactType. This essentially would make this State 'listen' to changes on a Widget it's inheriting data from.

The docs also suggest that it could be useful if you need to do network calls (or any other expensive action) when an InheritedWidget updates.

build()

It is a required, @override and must return a Widget.

didUpdateWidget

didUpdateWidget() is called if the parent widget changes and has to rebuild this widget (because it needs to give it different data), but it's being rebuilt with the same runtimeType, then this method is called.

This is because Flutter is re-using the state, which is long lived. In this case, required is to initialize some data again, as one would in initState().

If the state’s build() method relies on a Stream or other object that can change, unsubscribe from the old object and re-subscribe to the new instance in didUpdateWidget().

Flutter always called build() after this, so any subsequent further calls to setState is redundant.

@override
void didUpdateWidget(Widget oldWidget) {
if (oldWidget.importantProperty != widget.importantProperty) {
_init();
}
}

setState()

The ‘setState()’ method is called often from the Flutter framework itself and from the developer.

It is used to notify the framework that “data has changed”, and the widget at this build context should be rebuilt.

setState() takes a callback which cannot be async. It is for this reason it can be called often as required, because repainting is cheap :-)

void updateProfile(String name) {
setState(() => this.name = name);
}

deactivate()

deactivate()’ is called when State is removed from the tree, but it might be reinserted before the current frame change is finished. This method exists basically because State objects can be moved from one point in a tree to another.

dispose()

dispose()’ is called when the State object is removed, which is permanent.

This method is where to unsubscribe and cancel all animations, streams, etc.

mounted is false

The state object can never remount, and an error is thrown is setState() is called.

How Can You Contribute?

Show Your Support

Press the clap button below if you liked reading this post. The more you clap the more it motivates me to write better!

--

--