DEV Community

Cover image for Understanding and applying the State pattern in Flutter (Part 1)
Pedro Bueno for Cuscuz com Código

Posted on

Understanding and applying the State pattern in Flutter (Part 1)

When developing frontend applications, we often come across the idea of states: whether it's an application, a screen or a specific flow. This adds a lot of information and reactivity, and we end up calling this process "state management", even though we don't fully understand what a state is or how it affects our application. In this article, we'll look at a problem, a solution and its implementation, deepening our understanding of state management.

Topics

  1. Problem
  2. Solution
  3. Implementation

Problem

Let's start with an example

"In the contact list, we're separating our business logic from the UI, isn't that cool?!"

-Yes, but let's think about it: will the screen's content change depending on some value? If so, what values would those be?

"From that contact list and the user's customized name that are loaded afterwards, but that's it"

-I see, but would there be any loading or error components displayed?

"Yes, yes. If it's loading, we display a skeleton. If there's an error, we display an error component with the error message and the try again button"

...

And so, a screen needs to listen to a few variables: the contact list, the user's customized name, perhaps a boolean indicating whether it's loading and an error message (which would be nullable). Quite a lot for a simple screen, isn't it? This scenario is more common than it sounds.

If a state is a value that affects the flow of a system, our contact screen - the system in question - has more states than necessary. In the end, if everything is a state, then nothing is a state. The concept loses its relevance and only adds complexity to the code.

But what does this affect?

In this scenario, the screen would have to have several reactive components (ValueListenableBuilder, ListenableBuilder, BlocBuilder, Obx... take your pick), but we can still imagine what something inevitable would look like: an AppBar that displays the user's custom name, a skeleton that may or may not be displayed, a list and an error component. On a mobile device.

Now understand that this behavior can be repeated in a similar way on other screens, also considering that the flutter reloads the current screen during navigation.

We end up dealing with 2 main problems:

  1. Too many things being rendered: Flutter has improved its performance a lot, but the more things being rendered in parallel, the more hardware it will take
  2. Code reading and debugging: there is no single way to update the screen, any change to these variables will trigger rebuilds asynchronously, one function being executed can affect the work of another function.

Who can help us?

Solution

The way forward became clearer, but when I came across these problems during development, the use of the State Design Pattern became obvious. It is so recommended that BLoC - a reference library in the Flutter environment - already uses it by default.

What is the State pattern?

This format is based on something that, if you've studied Computer Science, you've probably heard of: finite state machine, which although it sounds complex is like a door that only has the state of open or closed and that's it, or Super Mario: standing still, walking or jumping.

Just as object-oriented programming is a simplification of today's world, everything in a way is also a finite state machine. And so is your screen.

This standard will simplify and create classes that represent these states: if loading, loading state, if error, error state, if data loaded....? Loaded state. Each with the information that is relevant to each state, not least because there is no need for a loaded state to have an error message that it won't use.

Drawing

So let's make a quick diagram to understand the flow of our screen: we'll have 3 main states:

  1. LoadingState → To display the skeleton
  2. ErrorState → To display the error message and the try again button
  3. LoadedState → To display the list and the user's name

In particular, I would recommend using a context prefix (e.g. ContactsLoadingState) so as not to confuse if other screens also use the pattern, but as we only have one context here I'll keep it that way.

In addition, I also suggest adding an InitialState, which would be the kick that starts the flow so that we can understand the transitions.

Regarding flows, we can follow a few rules:

  1. LoadingState can trigger LoadedState OR ErrorState, only one or the other.
  2. LoadedState can only trigger LoadingState (no error display on an already loaded screen).
  3. ErrorState can only trigger LoadingState (if the user tries again)
  4. InitialState can only trigger LoadingState (first component displayed is the skeleton)

From this we have this flow:

Implementation

And it's from this flow that we'll implement a solution on this contact screen on the next article.

See you later!

Sources:

https://refactoring.guru/pt-br/design-patterns/state

https://dev.to/adryannekelly/entendendo-state-pattern-flutter-3anp

https://en.wikipedia.org/wiki/Finite-state_machine

Top comments (0)