Save the expensive rebuild of apps using ValueNotifier in flutter

Santhosh Adiga U
4 min readOct 3, 2024

--

Photo by Kelly Sikkema on Unsplash

What is setState()?

In Flutter, setState() is like a magical incantation that triggers a UI update. When you call setState(), you’re telling Flutter: “Hey, something has changed! Rebuild the affected part of the UI.”

Specifically, setState() does two things:

  • It marks the widget as needing to be rebuilt.
  • It schedules a rebuild of the widget tree during the next frame.

How Does setState() Work?

Imagine you have a stateful widget (a widget that can change dynamically). Inside that widget, you define some mutable variables (state). When you want to update the UI based on changes in those variables, you call setState().

Flutter then does its magic:

  • It rebuilds the widget (and its subtree) associated with that state.
  • The new state values are reflected in the updated UI.

When to Use setState()?

  • Carefully! While setState() is powerful, it’s also a double-edged sword. Here’s how to wield it wisely:
  • Localize the setState() Call: Only use setState() where necessary. If a small part of your UI needs updating, call setState() only for that subtree. Don’t trigger unnecessary rebuilds elsewhere.
  • Avoid High-Up Calls: If a change affects only a specific widget or a small portion of your widget tree, don’t call setState() high up in the widget hierarchy. Keep it localized.
  • Think Performance: Frequent setState() calls can impact performance. Flutter is efficient, but excessive rebuilds can slow things down. Optimize where possible.
  • Use State Management Libraries: For complex apps, explore state management solutions like Provider, Riverpod, or BLoC. They help manage state more efficiently.

Where to Avoid setState()?

  • Avoid It When Not Needed: If a variable doesn’t affect the UI, skip setState(). No need to rebuild if it won’t be visible.
  • Network Calls and Heavy Computations: Don’t put network requests or heavy computations directly inside setState(). Fetch data elsewhere and then update the state.
  • Animations: For smooth animations, consider using AnimationController or other animation libraries instead of frequent setState() calls.

Let’s explore how ValueNotifier and the ValueListenableBuilder can be used as an alternative to setState() for efficient state management and UI updates.

Photo by Markus Spiske on Unsplash

ValueNotifier Basics:

What is ValueNotifier?

  • A ValueNotifier is a lightweight class provided by Flutter that extends ChangeNotifier. It’s designed to hold a single value and notify listeners when that value changes.
  • Unlike setState(), which triggers a full widget rebuild, ValueNotifier allows more granular updates. It’s like having a private story on Instagram—only your closest friends (the listeners) know about your updates.

When to Use ValueNotifier:

  • Use ValueNotifier when you have a simple piece of mutable state that needs to be tracked and updated reactively.
  • It’s particularly useful for scenarios where you want to avoid rebuilding the entire widget tree when only a specific part of it needs updating.

ValueListenableBuilder:

This widget is the perfect companion for ValueNotifier. It listens to changes from a ValueNotifier and rebuilds its child widget accordingly. Think of it as a specialized widget that knows how to dance to the rhythm of your ValueNotifier.

Here’s how it works:

  • Wrap your widget subtree with a ValueListenableBuilder.
  • Pass in the ValueNotifier you want to listen to.
  • Inside the builder callback, define what should be rebuilt based on the updated value.
  • Voilà! Efficient updates without unnecessary rebuilds.

Example: Using ValueNotifier and ValueListenableBuilder:

Let’s say we have a simple counter app. Instead of using setState(), we’ll use ValueNotifier:

import 'package:flutter/material.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}

class HomePage extends StatelessWidget {
final ValueNotifier<int> _counter = ValueNotifier<int>(0);

void _incrementCounter() {
_counter.value++; // Update the value
}

@override
Widget build(BuildContext context) {
print('HomePage built');
return Scaffold(
appBar: AppBar(title: const Text('ValueNotifier Counter')),
body: Center(
child: ValueListenableBuilder<int>(
valueListenable: _counter,
builder: (context, value, child) {
return Text('Count: $value');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
child: const Icon(Icons.add),
),
);
}
}

In this example:

  • We create a ValueNotifier<int> called _counter.
  • The UI updates only the Text widget inside the ValueListenableBuilder when the counter value changes.
  • No unnecessary rebuilds of the entire widget tree!

Remember the Key Points:

  • Use ValueNotifier for simple state management needs.
  • Pair it with ValueListenableBuilder to update specific parts of your UI efficiently.

--

--

Santhosh Adiga U
Santhosh Adiga U

Written by Santhosh Adiga U

Founder of Anakramy ., dedicated to creating innovative AI-driven cybersecurity solutions.

No responses yet