Sitemap
Saqlain Ali Shah

Saqlain Ali Shah

I am Saqlain Ali Shah and I'm working as a flutter developer on Upwork and Fiverr. I belong the beautiful land of Gilgit Baltistan.

Flutter ListView Optimization with Riverpod: Avoiding Unnecessary Rebuilds

3 min readJun 7, 2025

--

Efficient state management is key to building scalable, high-performance Flutter applications — especially when working with dynamic UI elements like ListView.builder. A common performance bottleneck is unnecessary widget rebuilds when list data changes. In this article, we’ll dive into a fully optimized Flutter example using Riverpod that avoids this issue through smart architecture.

🧱 The Problem: Unnecessary Rebuilds

Imagine a list of buttons, each displaying a number. When a button is pressed, its value increases — simple, right? But in many naive implementations, updating one item causes every widget in the list to rebuild, leading to performance issues, especially with large lists.

Press enter or click to view image in full size
Flutter app optimization

🎯 The Goal

We want:

  • ✅ A scrollable list of buttons.
  • ✅ Pressing one button should only rebuild that item, not the whole list.
  • ✅ Use Riverpod for clean and predictable state management.
  • ✅ Maintain immutability and performance best practices.

✅ The Optimized Solution

Here’s the code that solves the problem efficiently:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
runApp(ProviderScope(child: MaterialApp(home: HomeScreen())));
}

final elements = StateProvider<List<int>>(
(ref) => List.generate(50, (index) {
return index;
}),
);
final _index = StateProvider<int>((ref) => 0);

class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});

@override
Widget build(BuildContext context) {
print("Rebuilds.......");
return Scaffold(
appBar: AppBar(title: Text("App optimization")),
body: Consumer(
builder: (context, ref, child) => ListView.builder(
itemCount: ref.watch(elements.select((item) => item.length)),
itemBuilder: (context, index) {
print("Listview Items rebuild $index");
return Padding(
padding: const EdgeInsets.all(8.0),
child: ProviderScope(
overrides: [_index.overrideWith((ref) => index)],
child: buildButton(),
),
);
},
),
),
);
}
}

buildButton() {
return Consumer(
builder: (context, ref, child) {
final indexOfItem = ref.read(_index);
return ElevatedButton(
onPressed: () {
print("Length of List ${ref.read(elements).length}");
final list = [...ref.read(elements)];
int val = indexOfItem + 10;
list[indexOfItem] = val;
ref.read(elements.notifier).state = list; // Assign new list to state
},
child: Text("data ${ref.watch(elements)[indexOfItem]}"),
);
},
);
}

🚀 How It Works

1. Minimized Rebuilds with `.select`

ref.watch(elements.select((list) => list.length))

Only watches the length of the list, not the content. The ListView.builder won’t rebuild unless items are added or removed.

2. Scoped Providers Per List Item

ProviderScope(overrides: [_index.overrideWith((ref) => index)])

This technique allows each button to access its own index via a local override, effectively creating isolated rebuild zones.

3. Immutable State Updates

final list = [...ref.read(elements)];

By copying the list before modifying, we avoid mutating the state directly. This adheres to best practices and ensures proper state detection in Riverpod.

4. Only the Affected Button Rebuilds

Each button uses its scoped _index, and the Consumer around it listens only to the item at that index. This means:

  • Pressing “data 2” only rebuilds that specific button.
  • “data 0”, “data 1”, “data 3”, etc., remain untouched.

📈 Performance Benefits

| Technique | Benefit |

Get Saqlain Ali Shah’s stories in your inbox

Join Medium for free to get updates from this writer.

| — — — — — — — — — — — — — | — — — — — — — — — — — — — — — — — — — — — — |

| .select() | Prevents full-list rebuilds on small changes |

| Scoped provider overrides | Isolates rebuilds to the changed item |

| Immutable list updates | Ensures safe, predictable state transitions |

| StatelessWidget + Consumer | Avoids unnecessary stateful widget overhead |

Listview optimization

🧪 Debug Output (Optional)

Use print() statements to verify:

  • "Rebuilds......." is only printed once when the screen builds.
  • "ListView item builder called for index X" is printed once per item.
  • Tapping a button prints only that index’s rebuild info.

🧼 Final Thoughts

This example showcases a clean and scalable approach to managing dynamic lists in Flutter using Riverpod. With scoped state and precise rebuild control, your UI becomes faster, more efficient, and easier to maintain — especially in large applications.

--

--

Saqlain Ali Shah
Saqlain Ali Shah

I am Saqlain Ali Shah and I'm working as a flutter developer on Upwork and Fiverr. I belong the beautiful land of Gilgit Baltistan.