Understanding the Flutter Lifecycle: Widget States (Part 2)

Understanding the Flutter Lifecycle: Widget States (Part 2)

FlutterPulse

This article was translated specially for the channel FlutterPulseYou'll find lots of interesting things related to Flutter on this channel. Don't hesitate to subscribe!🚀

Introduction

Introduction

In Flutter, widgets are the building blocks of the user interface. They define how the UI looks and reacts to user interactions. Understanding the difference between Stateless and Stateful widgets is fundamental for building efficient and dynamic Flutter applications. This article breaks down the characteristics of both types of widgets, their differences, and how and when to use them in your Flutter projects.

What is a State?

Before discussing these two types of widgets, first we need to get to know with the term State. Why is that? Because our widgets will constantly be dealing with State. So, what is State?

State is the data or condition that determines how a widget looks or behaves at a particular moment. Since Flutter uses the OOP (Object-Oriented Programming) paradigm, the state is usually a property of a class. Example:

class ExampleWidget extends StatelessWidget{
final String _title;
...
}

StatelessWidget VS StatefulWidget

  • StatelessWidget: A StatelessWidget is used for static content that doesn't change once it's built, like displaying text or icons. It doesn't manage any internal state, and it only rebuilds when the parent widget changes.
    When you should Use it:
    When you need a static widget that wouldn't change or doesn't have internal state.
  • StatefulWidget: A StatefulWidget is used for content that can change over time, like a counter or form input. It has internal state, and it can rebuild itself whenever its state is updated.
    When you should Use it:
    Use it for dynamic content that needs to be updated, like a counter or a form.

Key Differences:

StatelessWidget

A Stateless Widget is a state created ONLY ONCE, then it can update values but not state explicitly. A StatelessWidget cannot change (immutable), making the widget more static and with limited interaction.

StatelessWidget Lifecycle

In a StatelessWidget, it has a simple lifecycle. Why is that? Because it is immutable, meaning there is no state to manage. Once it is built, the StatelessWidget will not change. The widget must be rebuilt if you want to change its values. Example of implementation of StatelessWidget:

class Heading extends StatelessWidget {
final String text;

Heading({this.text});

@override
Widget build(BuildContext context){
return Text(
text,
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
),
);
}
}

StatefulWidget

A StatefulWidget, on the other hand, can change its appearance or behavior based on dynamic data or user input. Stateful widgets maintain a mutable state, which allows the widget to rebuild itself when the state changes. These widgets are ideal for cases where the UI needs to be updated based on user interaction or other external changes.

initState()

The first method that will be called in a state object is initState(). By default, this method is not explicitly available when creating a stateful widget. However, this method is commonly used to declare variables, data, properties, make API calls, etc. To use it, you need to override it first and include the actions you want to execute before the widget is displayed.

class MyStatefulWidget extends StatefulWidget {
...
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
late final int _index;

@override
void initState() {
super.initState();
_index = 0;
}
...
}

didChangeDependencies()

This method will be called whenever the dependency values of the widget change. These dependencies include the values of variables or constructor properties. When these values change, this method will be triggered and will execute the actions inside of it.

class MyStatefulWidget extends StatefulWidget {
...
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {

@override
void didChangeDependencies() {
super.didChangeDependencies();
// Your logic when the dependencies did change
}
...
}

build()

The method you will always work with when creating a widget is the build() method. This method is where you arrange your widgets. It will always be called whenever there is a change in state for example when you called the setState() method.

class MyStatefulWidget extends StatefulWidget {
...
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {

@override
Widget build(BuildContext context) {
return const Container();
}
...
}

setState()

A method that tells the Flutter framework to rebuild a widget. By calling this method, you trigger a rerun of the build() method, which reconstructs the widget tree. This process enables dynamic UI updates based on state changes. You can call this method whenever you want to update the widget's appearance, such as after user input, button presses, data fetches, or other state changes that affect the UI.

class MyStatefulWidget extends StatefulWidget {
...
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _index = 0;

@override
Widget build(BuildContext context) {
return TextButton(
onPressed: () => setState(() {
_index = _index + 1;
}),
child: const Text("Increment Button with Index: $_index"),
);
}
}

didUpdateWidget()

It is called when the properties of a widget change. A common scenario for this is when the parent widget passes new values or changes the property values of a child widget via the constructor. This method allows you to react to changes in the widget's configuration or state, and it is called before the widget's build method is triggered. It gives you an opportunity to perform actions or update the state based on the new widget properties. For example, if a parent widget passes new data to a child widget, didUpdateWidget() can be used to handle those changes, ensuring that the child widget properly responds to the updated values.

class MyStatefulWidget extends StatefulWidget {
...
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {

@override
void didUpdateWidget(covariant MyStatefulWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// Code when the widget did updated
}
...
}

dispose()

This method is called when the widget is about to be disposed of or removed. It is typically used to release certain resources, such as animations, network processes, and other cleanup tasks. Please note that you have to put your code above the super.dispose()

class MyStatefulWidget extends StatefulWidget {
...
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
...

@override
void dispose() {
// additional code

super.dispose();
}
}

Report Page