State Management in Flutter: Beginner’s Guide I


Table of Contents

Understanding state management is crucial for creating robust, maintainable, and scalable applications. Flutter, renowned for its beautiful user interface and rapid development, offers several possibilities to manage the state effectively and according to the type of project we are developing.

In this publication, we will delve into the basics of the world of state management in Flutter, exploring essential concepts and different techniques that can help in choosing the one that best fits our level of experience, the team, and the project in general.

Understanding State Management in Flutter

In Flutter, state management represents the process in which data will be updated or rebuilt in the user interface (UI), allowing it to change its behavior over time as often as required in the application. This facilitates the communication and updating of the state of one or more widgets, located in any path of the project tree structure, either at the same level as the widget, with other widgets, animations, repositories, network requests, or other events.

When is State Management Necessary?

Undoubtedly, in the beginning, dealing with state management is not an easy task, but as an application grows in complexity, we begin to experience certain inconveniences that can be easily identified. For example, we observe an excess of callbacks and repetitive code; we need to access data located in different paths of the project, we face errors in the reconstruction of UI components, we must manage local storage, reuse widgets, and consume external data, among other events.

Without a proper strategy, managing the state can become a daunting task, resulting in code that is difficult to maintain and prone to errors.

After being clear about the requirements for state management, it is advisable to consider the following key points before choosing a strategy. These are:

  • Which Widget is interested in consuming that data?
  • If it is only a widget, it is recommended to use the local state (Ephemeral state).
  • If there are several widgets, it is advisable to use a status manager.

For clarity, here are some examples of common situations within an application that require state management:

  • UI Interactions: any type of event that arises from interaction with the screen, be it touches, scrolls, or inputs, will reflect changes in the state of the UI components.
  • Navigation between screens: it is likely to be necessary to maintain and share relevant data at the transition between screens.
  • Forms: the data entered by the user needs to be validated and managed before being processed.
  • Animations: monitor the progress of the animation components and their subsequent UI update.
  • External Data: the consumption of an API, DB, or real-time services, will be reflected in the UI updates.
  • Life cycle: when preserving or restoring data, state management will help with these processes, such as pausing and resuming.

Key Concepts in State Management

SetState

ThesetState()method is a crucial function for managing the state of a widget and triggering user interface updates. It is part of theStateclass and is used to indicate that the state of a widget has changed, which triggers a rebuild of the user interface.

Core Concepts

  • StatefulWidgets:setState()is mainly used in conjunction with stateful widgets (StatefulWidget). These widgets have a mutable state that can change over time and setState()is the mechanism for communicating these changes.
  • Immutable State: when the state needs to be changed, instead of modifying it directly, a new immutable object is created andsetState()is called to trigger the UI update.

How setState() works

When you want to modify the state within a State object, you must do so within the callback function passed tosetState(). This function is then executed within a new microtask, ensuring that the UI rebuild is performed efficiently.

ThesetState()method reports that the state of the widget has changed. Then, a rebuild of the widget’s subtree is scheduled, ensuring that the updated state is reflected in the UI.

Exploring Use Cases with setState()

  • User Interactions: setState() is used in response to user interactions, such as pressing buttons or gestures. When the user triggers an action, setState() is used to update the user interface.
  • Form Input Handling: setState() helps manage the state of form fields. When users enter or modify data, calling setState() ensures that the user interface reflects the latest input update.
  • Dynamic UI Elements: dynamic content, such as lists or grids, often rely on setState() to update their content. As data changes, calling setState() triggers a UI update, which displays the latest information.

Recommendations for the Use of setState()

  • Minimize the scope ofsetState()to only those parts of the UI that need to be updated. Excessive use may cause unnecessary rebuilds in the UI.
  • Passing a function as an argumentsetState()instead of directly modifying the state helps to correctly encapsulate state changes within the function.
  • Use conditional statements to determine the appropriate time to usesetState(). If you have multiple state changes, consider grouping them to minimize the use ofsetState(). This can help avoid unnecessary rebuilds in the UI and improve performance.
  • Respect the principle of immutability when usingsetState(). Creating new object instances with the updated values and passing them tosetState()help ensure a clean and predictable state management process.

InheritedWidget

InheritedWidgetis a widget that allows the propagation of data along the widget tree, being accessible from any widget descending from the ancestor. This solution is useful in situations where several widgets need to access the same information without having to explicitly pass parameters to the constructor repeatedly. The main feature of this widget is that when data changes in the inherited widget, only the dependent widgets are rebuilt.

Core Concepts

  • BuildContext: is an object containing information about the widget’s position within the widget tree. It references the path according to the hierarchy in the tree to indicate where the current widget context is located.
  • Widget Inheritance: when one widget contains another, the larger widget is said to be the “parent” and the smaller widget is the “child”. Then, the child widget inherits the properties and features of the parent widget.
  • Widget Rebuild: when the state of the application changes, the widget’sbuild()method is called to rebuild the UI. This method generates a new representation of the widget and compares it with the previous one. If there are differences, the UI is updated.

Exploring Use Cases with InheritedWidget

  • Theme and Styling: The widget can be used to propagate themes and styling information throughout the application. For example, you can create a widget containing color schemes, fonts, and other styling details.
  • Authentication State: In the case where we need to reuse the state of authentication that must be shared among several parts of the application, InheritedWidget provides an elegant and minimalistic solution.
  • Language Localization: When we need to create multilingual applications, using InheritedWidget helps to propagate the current language/locale information throughout the application’s widget tree.

Implementing InheritedWidget

To implement an InheritedWidget, it is necessary to create a subclass that extendsInheritedWidget. This subclass must contain all the information to be shared and implemented. TheupdateShouldNotifymethod will help determine whether to notify the rebuild to its descendants when there are changes in the data.

Consuming InheritedWidget

To access the data shared within the descendant widgets, theofmethod is used. This method provides access to the closest instance of the inherited widget and allows retrieving the data from the ancestor widget. For example:

Recommendation

It is recommended to understand the concepts of InheritedWidget before starting to use packages. This gives us a deeper understanding of how to seamlessly access and update information between the different paths in the widget tree. Understanding how the framework works to communicate data between widgets hierarchically will also be helpful before tackling the use of packages for state management, which will be explored in the second part of this guide.