Today we’re going to talk about one of the most important concepts in Flutter: the difference between Stateless and Stateful widgets. These are two of the most common widget types you’ll work with while developing Flutter apps. If you’ve ever created a new Flutter project, you’ve already seen them in the default counter example.
So what exactly are they? In simple terms, these two widgets control whether the UI can change during the lifetime of the app. A StatelessWidget is defined as a widget that does not require mutable state. A StatefulWidget, on the other hand, is a widget that does have mutable state. The word “mutable” simply means something that can change. So when we say a widget has mutable state, we mean it can update dynamically while the app is running. A stateless widget cannot change on its own once it has been built.
To make this clearer, let’s walk through a simple example: a coin flip app. The app will display whether the coin is “Heads” or “Tails” in the app bar, and it will have a floating action button at the bottom that flips the coin when pressed. This small example clearly demonstrates the difference between stateless and stateful widgets.
We begin by creating a basic app structure with a MaterialApp and a Scaffold. Inside the Scaffold, we add an AppBar with a title that says, “The coin is …” followed by either Heads or Tails. To decide which one to show, we use a simple integer variable called coinHead and a ternary operator (a shortened if–else statement). If coinHead equals 1, we display “Heads.” Otherwise, we display “Tails.” Since we initially set coinHead to 0, the app shows “Tails” when it first runs.
Next, we add a floating action button. When pressed, it should randomly generate either 0 or 1 using Dart’s math library. In the console, we can see the value switching between 0 and 1 each time we press the button. However, something interesting happens: even though the value changes internally, the app bar text does not update. It continues to show “Tails,” even when the random number becomes 1.
At this point, we might think to use setState() to trigger a rebuild. But when we try to use setState() inside a StatelessWidget, we get an error. The message tells us that setState is not defined for this class and that the widget is marked as immutable. In other words, a StatelessWidget is not allowed to change after it has been created. That’s the key limitation.
The solution is simple: convert the widget from stateless to stateful. Both Android Studio and VS Code make this easy with a quick refactor option. Once the widget becomes a StatefulWidget, we can wrap our state changes inside setState(). Now, every time we press the button, the random value updates and the UI rebuilds. The app bar correctly switches between “Heads” and “Tails.”
In summary, use a StatelessWidget when your UI does not need to change after it is built. Use a StatefulWidget when your UI depends on values that can change over time. While the concept can become more advanced as your apps grow, this simple coin flip example gives you a solid foundation for understanding how state works in Flutter.
