Flutter Performance Best Practices

Alvaro Armijos
7 min readApr 2, 2023

--

This time we are going to talk about the best practices to obtain better performance in applications developed with Flutter.

General Overview

Why is the issue of performance in an Application so important?

Let’s see the example of PedidosYa. If you opened the PedidosYa application at the beginning of 2021, you probably got frustrated with the time it took to load. It is understandable since the first page took about 17 seconds on average between Android and iOS users.

The problem of the Home of PedidosYa lay mainly in the following points:

  • The load of that page asked for a lot of contextual information so that all the other flows of the app can work when they are called.
  • Much of the business logic of that screen was in the frontend which made iterating any behavior time-consuming.

What they did was that most of the responsibilities go to the backend and the mobile applications are only going to know how to draw each component that comes to them. In other words, everything is orchestrated by a service that ultimately decides what to draw on the screen, in what order it wants to place these components and when it is going to load that content. Here you can read the article.

Obviously this was all a multi-team effort, but what we can do on our mobile development side to give users the best experience is apply best practices.

Flutter performance

Flutter applications are performant by default, so you only need to avoid common pitfalls to get excellent performance. How you design and implement your app’s UI can have a big impact on how efficiently it runs.

These best practice recommendations will help you write the most performant Flutter app possible.

Control build() cost

Here are some things to keep in mind when designing your UI:

  • Avoid repetitive and costly work in build() methods since build() can be invoked frequently when ancestor widgets rebuild.
  • Avoid overly large single widgets with a large build() function. Split them into different widgets based on encapsulation but also on how they change
  • When setState() is called on a State object, all descendent widgets rebuild. Therefore, localize the setState() call to the part of the subtree whose UI actually needs to change. Avoid calling setState() high up in the tree if the change is contained to a small part of the tree.

Let’s see this example, we want that when the user presses the icon, only the color of this icon changes.

So if we have all this UI in a single widget, when the icon is pressed, it will update the whole UI. What we can do is separate the icon into a StatefulWidget.

  • Use const constructors on widgets as much as possible, since they allow Flutter to short-circuit most of the rebuild work. To be automatically reminded to use const when possible, enable the recommended lints from the flutter_lints package.
  • To create reusable pieces of UIs, prefer using a StatelessWidget rather than a function.

As Remi Rousselet, the creator of Riverpod, Provider and other packages says. “Classes have a better default behavior. The only benefit of methods is having to write a tiny bit less code. There’s no functional benefit”

Minimize use of opacity and clipping

Opacity is another expensive operation. Here are some tips you might find to be useful.

  • Use the Opacity widget only when necessary.
  • To implement fading in an image, consider using the FadeInImage widget
  • To create a rectangle with rounded corners, instead of applying a clipping rectangle, consider using the borderRadius property offered by many of the widget classes.
  • Using SizedBox instead of Container

There are multiple use cases where you will require to use a placeholder. Here is the ideal example below:

The Container is a great widget that you will be using extensively in Flutter. Container() brodens up to fit the constraints given by the parent and is not a const constructor.

On the contrary, the SizedBox is a const constructor and builds a fixed-size box. The width and height parameters can be null to specify that the size of the box should not be constrained in the corresponding dimension.

Thus, when we have to implement the placeholder, SizedBox should be used rather than using a container.

Implement grids and lists thoughtfully

How your grids and lists are implemented might be causing performance problems for your app.

When building a large grid or list, use the lazy builder methods, with callbacks. That ensures that only the visible portion of the screen is built at startup time.

To specify each item’s extent, you can use either itemExtent or prototypeItem. Specifying either is more efficient than letting the children determine their own extent because the scrolling machinery can make use of the foreknowledge of the children’s extent to save work, for example when the scroll position changes drastically.

What no one says about Flutter: Theme.of(context)

Finally, we are going to talk about a topic that is rarely talked about in the Flutter community.

In one app we found that we were having strange behavior and the app was rebuilt multiple times. What was happening? Let’s see an example to understand it.

When we press the FAB, we see that the build() method is executed 11 times. This is something we don’t want to happen in our application and affects the performance.

So we don’t know if it’s a desired behavior, if Google really wants the Theme.of(context) works in the reactive way(it could be say) or we are using the Framework wrongly or it’s a bug that is rarely talked about. In fact, using Theme.of(context) isn’t a bad practice, since in this way we access the current theme of the application globally.

We also found that there is an issue in the repository, but until this moment there isn’t an official statement from Flutter.

How can we fix it? For this we can begin to apply the previous recommendations, which isn’t something that I am inventing, but they are guidelines that Flutter itself gives us to obtain better performance.

As we can see, applying these simple recommendations we obtain better performance. It seems that it was not a big change but imagine how efficient we are making this simple App, it is no longer rebuilt 11 times as it happened before, but now it’s rebuilt only once, which is the desired and expected behavior.

So as a recommendation to all people who use Flutter in their applications, check if using Theme.of(context) is causing the app to be rebuilt several times, since as I said it’s common to access the theme in this way and try to apply the good practices we reviewed.

If you like it, you can Buy Me A Coffee!

--

--

Alvaro Armijos

Electronic and Telecommunications Engineer | #Flutter Developer 💙 | Always eager to learn | https://www.linkedin.com/in/alvaro-armijos-sarango/