I am developing using Flutter.
I encountered some unexpected behavior with widget rebuilds and ScrollControllers.
Below is the program that reproduces the issue.
In my understanding, I thought that widget rebuilds due to state changes are performed by the Flutter engine every frame. Therefore, when running the program below and scrolling the screen, I expected the process to proceed alternately as "scroll event -> rebuild -> scroll event -> rebuild -> …".
However, contrary to my expectation, scroll events may occur continuously. Moreover, this behavior often happens right after the first scroll after the screen is initialized. An example of the execution result with my comment is provided below.
Could anyone please explain the reason behind this?
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int counter = 0;
final _controller = ScrollController();
@override
void initState() {
super.initState();
_controller.addListener(() {
print("onScroll event");
setState(() {
counter = counter + 1;
});
});
}
@override
Widget build(BuildContext context) {
print("build: $counter");
return Scaffold(
body: ListView.builder(
itemCount: 1000,
controller: _controller,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item ${index + 1}'),
);
},
),
);
}
}
flutter: build: 0
flutter: onScroll event
flutter: build: 1
flutter: onScroll event
flutter: onScroll event // Why do those events run before rebuild?
flutter: onScroll event //
flutter: build: 4
flutter: onScroll event
flutter: build: 5
flutter: onScroll event
flutter: build: 6
flutter: onScroll event
flutter: build: 7
flutter: onScroll event
flutter: build: 8
flutter: onScroll event
flutter: build: 9
flutter: onScroll event
flutter: build: 10
flutter: onScroll event
flutter: build: 11
flutter: onScroll event
flutter: build: 12
flutter: onScroll event
flutter: build: 13
flutter: onScroll event
flutter: build: 14
flutter: onScroll event
...
Thanks 🙂
2
Answers
Inspired by Hamed's answer, I came up with the following code that rebuilds the widget immediately after a scroll event. This works as expected, but I'm creating variables outside of the widget, which I don't think is smart.
Scrolling a ListView in Flutter continuously generates scroll events by the attached ScrollController. These events are processed asynchronously, and the widget rebuilds triggered by the setState method are scheduled for the next frame. As a result, you may observe multiple scroll events happening before the widget is actually rebuilt, leading to the unexpected behavior you’re experiencing. This is because the scroll events are dispatched immediately, while the rebuild process is scheduled later. Once the rebuild is completed, you will see the updated widget reflecting the changes.
You can change your code like this: