Flutter Hook widgets over Stateful widgets for better app performance

Everything in Flutter is made up of widgets. In this article, we will explore the different types of widgets(Stateless and Stateful)and also look into Hook widgets, an extension to Stateless widgets. We will also explore the differences and see how Hook widgets can be used over Stateful widgets to improve app performance.
What is a widget in Flutter?
Flutter widgets are built using a modern framework that takes inspiration from React. The central idea is that you build your UI out of widgets. Widgets describe what their view should look like given their current configuration and state.
If any action on the UI requires the widget to be rebuilt, then this is where you should use Stateful widget. But if the widget only displays data, and any action on the UI does not need to rebuild the widget, then you can use the Stateless widget.
Stateless widgets are the easy to use widgets and you cannot really do much with them in terms of adding logic, because as the name suggests, they are stateless. Once the UI builds, no action in the UI can trigger a rebuild of the widget.
But when we talk about Stateful widgets, there’s a lot that happens behind the scenes, any change in the UI triggers the widget to rebuild.
Let us consider the example of a Counter app,
1. Counter app using Stateful widget
In this app, I can change the counter value by tapping the “-” and the “+” buttons. Every tap changes the UI and rebuilds the widget to update the value of the counter. I will store the value of the counter and display it in two places in the UI

The code for the above screen is
Note that I have heavily used setState wherever I am updating the counter value. This is because I am informing the widget to update the value of the variable and rebuild the widget with the updated value.
If I do not include setState, then the counter value will get updated but the UI will not be rebuilt as a result of which the UI will not change at all and you will only see the old counter value in the UI.
While this is a straightforward technique to manage states, stateful widgets may not be an optimal choice when building complex widgets. This is because continuously rebuilding the widgets can bring down the app performance and causes the app to consume lots of resources during rebuild.
This is when you should consider using Hook widgets, an extension to Stateless widgets.
2. Counter app using Hook widgets

Hooks are a new kind of object that manage the life-cycle of a
Widget
. They exist for one reason: increase the code-sharing between widgets by removing duplicates.
When using Hooks, we only make use of one class, which makes it easy to maintain the code. The flutter_hooks gives us access to list of reusable hooks, some of which include:
- useEffect — Useful for side-effects and optionally canceling them.
- useState — Creates a variable and subscribes to it.
- useMemoized — Caches the instance of a complex object.
- useAnimationController — Creates an
AnimationController
which will be automatically disposed. - useTextEditingController — Creates a
TextEditingController
. - useTabController — Creates and disposes a
TabController
.
For the same counter example, when we use Hooks, the app runs in the same manner. However, the main difference is that we will be using the useState
hook for the counter variable which ensures that any change in the UI will be notified.
final count = useState(0);
- the count variable is an instance of ValueNotifier.
- useState — hook
- 0 —
initialData
To access the value of the variable count, use count.value
Whenever ValueNotifier.value updates, it will mark the caller HookWidget as needing a build. On the first call, it initializes ValueNotifier to
initialData
.initialData
is ignored on subsequent calls.
When the Hook widget renders for the first time, the ValueNotifier is set to initialData
, as the value changes, the subsequent calls will use the updated value.

In the above widget, the count value initially is set to 0, which comes from the initialData
that was set when initializing the hook. As the counter keeps updating, the ValueNotifier.value(count) changes and the widget rebuilds.
During the rebuild, the impact of performance is negligible since hooks allow you to remove the need for initState, dispose and setState, as everything related to the state is initialized with the hook functions in the build method.
useEffect()
To load data during the initState of a widget, or to dispose data, we use useEffect() hook.
Just like initState and dispose, useEffect() will be
- only called once during the lifetime of the widget,
- the return statement will be executed when the widget is disposed,
- the const [] means that until the widget is not disposed, don’t call the effect. There can be a list of parameters, and when one of the parameters change, the useEffect will call the callback again.
Below is the code for Timer with Hook widget:
Below is the code for Timer with Stateful widget:
Hooks are an easy way to maintain simple logic and I have used it in most of my projects. It avoids complex logic and promotes better app performance. If you wish to learn more about hooks, Flutter docs provide plenty of resources at https://pub.dev/packages/flutter_hooks
Feel free to leave a comment and share as much if you found it helpful!
Level Up Coding
Thanks for being a part of our community! Before you go:
- 👏 Clap for the story and follow the author 👉
- 📰 View more content in the Level Up Coding publication
- 🔔 Follow us: Twitter | LinkedIn | Newsletter
🚀👉 Join the Level Up talent collective and find an amazing job