Hedegare's Blog

Building - Learning - Optimizing - Growing

Beginner

Dart

Flutter

Flutter Widgets

Guide to Stateful and Stateless Widgets in Flutter

Central to Flutter’s functionality are widgets. Widgets are used to describe the structure and layout of your application’s elements, such as buttons, text fields, images and more. In Flutter, widgets can be classified into two categories: Stateful and Stateless. In this guide, we’ll explore what are Stateful and Stateless widgets, when and how to use them, and provide practical example to illustrate their applications.


If you are building an application with Flutter, your UI will be built out of widgets. Widgets are the building blocks of a Flutter app; in fact, everything in Flutter is a widget. The view of your app is a tree of widgets composed from simple elements (like text labels and buttons) to complex layouts and animations. Understanding widgets is a crucial step in building effective and dynamic Flutter applications.

Overview of what are widgets in Flutter

Before we start talking about Stateful and Stateless widgets, let’s talk a bit more about what are widgets in Flutter.

As mentioned above, in Flutter, widgets are components (similar to ReactJs) that represent the UI of your app. They form a hierarchical structure which defines the layout and organization of the UI elements.

Widgets are represented by immutable classes that are used to configure a tree of objects. This means that when a widget is created they cannot be changed. When the state of a widget changes (for example, by a user pressing a button), instead of Flutter modifying the widget, a new one is created.

We have seen before that an app in Flutter is a tree of widgets; so we can say that a widget can be composed of many other widgets. In other words, widgets can be composed together to build complex UIs from simple components. By taking this approach, you can create reusable widgets with an easier way to maintain the code.

With this in mind, let’s finally start talking about the two types of widgets: Stateful and Stateless.

Stateless widgets

Stateless widgets are immutable and do not hold any state; so they remain the same throughout its life time and are used for static content.

An example of this type of widget can be a text label or, as we’ll see now, can represent an item from a list.

example_stateless_widget.dart
1
class CustomListItem extends StatelessWidget {
2
final String title;
3
4
const CustomListItem({ super.key, required this.title });
5
6
@override
7
Widget build(BuildContext context) {
8
return Padding(
9
padding: const EdgeInsets.all(8.0),
10
child: Center(
11
child: Text(title)
12
),
13
);
14
}
15
}

As you can see, the content of the widget will never change; it’ll always be a text label with a title that is passed as a parameter.

With this code, everytime you create a list that just needs to present text, you can use this CustomListItem widget. We’ll now see how to use this widget with a Stateful widget.

Stateful widgets

Stateful widgets maintain a mutable state that can change over time; they are used for dynamic content that can update based on user interation or other events.

Examples of this type of widget are a checkbox, a form input or, as we’ll see, a dynamic list.

example_stateful_widget.dart
1
class CustomDynamicList extends StatefulWidget {
2
const CustomDynamicList({ super.key });
3
4
@override
5
State<CustomDynamicList> createState() => _State();
6
}
7
8
class _State extends State<CustomDynamicList> {
9
List<String> items = [];
10
11
void addItem() {
12
items.add("Item #${items.length + 1}");
13
setState(() {
14
items = [...items];
15
});
16
}
17
18
@override
19
Widget build(BuildContext context) {
20
return Column(
21
children: [
22
Expanded(
23
child: ListView.builder(
24
itemCount: items.length,
25
itemBuilder: (context, int index) {
26
return CustomListItem(title: items[index]);
27
}
28
)
29
),
30
Padding(
31
padding: const EdgeInsets.all(20.0),
32
child: ElevatedButton(
33
onPressed: () => addItem(),
34
child: const Text("Add item")
35
)
36
)
37
]
38
);
39
}
40
}

Here, we implemented a dynamic list widget, composed of the CustomListItem widget we created before; the dynamic part is how many CustomListItem are going to be created.

In this example, when the user presses the ElevatedButton a new item is added to the list items; since the list’s state has changed (by adding a new item to the list), the setState function is called triggering a redraw of the widget, which then creates a new CustomListItem (Stateless widget), passing just a string of text as the required title parameter.

Recap

We’ve explored the fundamental concepts of widgets and learned that they are the core building blocks of a Flutter application, providing a flexible and efficient way to create and manage UI components.

Stateless widgets (like CustomListItem we created before) are perfect for static content; while Stateful widgets (like CustomDynamicList) handle dynamic and changeable states within your app.

By understanding these principles and concepts, you can build robust, dynamic, and maintable Flutter apps.