The issue where the position of the displayed content on the page changes when the Flutter scrolling component slides down or up to load and add data
When I swipe the page to load data, the content displayed on the page changes, which is too bad for me. The effect I want is that when I swipe to the top or bottom of the page to load data, the content displayed on the page remains unchanged, rather than displaying newly loaded data content
Here is my code
class DemoChatListPage extends StatefulWidget {
const DemoChatListPage({super.key});
@override
_DemoChatListPageState createState() => _DemoChatListPageState();
}
class _DemoChatListPageState extends State<DemoChatListPage> {
List<String> messages = List.generate(20, (index) => 'Message $index');
final ScrollController _scrollController = ScrollController();
bool isLoadingMoreTop = false;
bool isLoadingMoreBottom = false;
double currentScrollOffset = 0;
@override
void initState() {
super.initState();
_scrollController.addListener(_scrollListener);
}
@override
void dispose() {
_scrollController.removeListener(_scrollListener);
_scrollController.dispose();
super.dispose();
}
void _scrollListener() {
if (_scrollController.position.atEdge) {
if (_scrollController.position.pixels == 0) {
// to top
_loadMoreTop();
} else {
// to bottom
_loadMoreBottom();
}
}
}
Future<void> _loadMoreTop() async {
if (isLoadingMoreTop) return;
currentScrollOffset = _scrollController.position.maxScrollExtent;
setState(() {
isLoadingMoreTop = true;
});
await Future.delayed(const Duration(seconds: 1));
List<String> newMessages = List.generate(10, (index) => 'New Message Top $index');
setState(() {
messages.insertAll(0, newMessages);
isLoadingMoreTop = false;
});
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent - currentScrollOffset);
});
}
Future<void> _loadMoreBottom() async {
if (isLoadingMoreBottom) return;
setState(() {
isLoadingMoreBottom = true;
});
await Future.delayed(const Duration(seconds: 1));
setState(() {
List<String> newMessages = List.generate(10, (index) => 'New Message Bottom $index');
messages.addAll(newMessages);
isLoadingMoreBottom = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Chat'),
),
body: Stack(
children: [
ListView.builder(
controller: _scrollController,
itemCount: messages.length,
itemBuilder: (context, index) {
return Container(
height: Random().nextInt(51) + 50,
color: Colors.blue[index % 2 == 0 ? 200 : 300],
child: ListTile(
title: Text(messages[index]),
),
);
},
),
if (isLoadingMoreTop)
const Positioned(
top: 0,
left: 0,
right: 0,
child: LinearProgressIndicator(),
),
if (isLoadingMoreBottom)
const Positioned(
bottom: 0,
left: 0,
right: 0,
child: LinearProgressIndicator(),
),
],
),
);
}
}
The content displayed during data loading The content displayed after data loading is completed
The issue where the position of the displayed content on the page changes when the Flutter scrolling component slides down or up to load and add data
When I swipe the page to load data, the content displayed on the page changes, which is too bad for me. The effect I want is that when I swipe to the top or bottom of the page to load data, the content displayed on the page remains unchanged, rather than displaying newly loaded data content
Here is my code
class DemoChatListPage extends StatefulWidget {
const DemoChatListPage({super.key});
@override
_DemoChatListPageState createState() => _DemoChatListPageState();
}
class _DemoChatListPageState extends State<DemoChatListPage> {
List<String> messages = List.generate(20, (index) => 'Message $index');
final ScrollController _scrollController = ScrollController();
bool isLoadingMoreTop = false;
bool isLoadingMoreBottom = false;
double currentScrollOffset = 0;
@override
void initState() {
super.initState();
_scrollController.addListener(_scrollListener);
}
@override
void dispose() {
_scrollController.removeListener(_scrollListener);
_scrollController.dispose();
super.dispose();
}
void _scrollListener() {
if (_scrollController.position.atEdge) {
if (_scrollController.position.pixels == 0) {
// to top
_loadMoreTop();
} else {
// to bottom
_loadMoreBottom();
}
}
}
Future<void> _loadMoreTop() async {
if (isLoadingMoreTop) return;
currentScrollOffset = _scrollController.position.maxScrollExtent;
setState(() {
isLoadingMoreTop = true;
});
await Future.delayed(const Duration(seconds: 1));
List<String> newMessages = List.generate(10, (index) => 'New Message Top $index');
setState(() {
messages.insertAll(0, newMessages);
isLoadingMoreTop = false;
});
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent - currentScrollOffset);
});
}
Future<void> _loadMoreBottom() async {
if (isLoadingMoreBottom) return;
setState(() {
isLoadingMoreBottom = true;
});
await Future.delayed(const Duration(seconds: 1));
setState(() {
List<String> newMessages = List.generate(10, (index) => 'New Message Bottom $index');
messages.addAll(newMessages);
isLoadingMoreBottom = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Chat'),
),
body: Stack(
children: [
ListView.builder(
controller: _scrollController,
itemCount: messages.length,
itemBuilder: (context, index) {
return Container(
height: Random().nextInt(51) + 50,
color: Colors.blue[index % 2 == 0 ? 200 : 300],
child: ListTile(
title: Text(messages[index]),
),
);
},
),
if (isLoadingMoreTop)
const Positioned(
top: 0,
left: 0,
right: 0,
child: LinearProgressIndicator(),
),
if (isLoadingMoreBottom)
const Positioned(
bottom: 0,
left: 0,
right: 0,
child: LinearProgressIndicator(),
),
],
),
);
}
}
The content displayed during data loading The content displayed after data loading is completed
Share Improve this question edited Feb 17 at 6:27 DarkBee 15.6k8 gold badges72 silver badges116 bronze badges asked Feb 17 at 4:17 Gasong PonGasong Pon 112 bronze badges New contributor Gasong Pon is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.1 Answer
Reset to default 0The problem in your case is the variable height of each Container which is causing a small bump in the current scroll position.
I removed your line height: Random().nextInt(51) + 50
and it works like a charm.
Otherwise you have to precompute all your heights and jumpTo according to the updated direction + the sum of all new heights.
Best of luck.