Closed nikita488 closed 2 months ago
Please provide a reproducible example.
Why do you need to keep the scroll position when the list goes from no data to data?
In addition, it should be noted that the prerequisite for the function of keeping the scroll position is that there must be an item in the list that can be used as a reference.
Here is the repo with example: https://github.com/nikita488/scrollview_observer_test I don't need to keep position on list initializing, the problem right now is that when it loads after some time, and then when i scroll up and try to add new item, firstly it jumps, rebuild my list with shrinkWrap setted to false and then after I add more items, it not jumps and works as expected.
I haven't reproduced the problem you mentioned, maybe you can record a video demonstration.
and then when i scroll up and try to add new item, firstly it jumps
I see you have set ..fixedPositionOffset = 8.0
. Please confirm whether the offset of the list does not exceed this value when the problem you mentioned occurs.
And I noticed that the initial list data has made the list exceed one screen, but chatObserver.isShrinkWrap
is still false, which is incorrect.
You can make the following adjustments (I am not familiar with the use of bloc, you can adjust it more reasonably).
class _MainAppState extends State<MainApp> {
...
@override
void initState() {
...
super.initState();
+ context.read<MessageListCubit>().chatObserver = chatObserver;
}
...
}
class MessageListCubit extends Cubit<MessageListState> {
...
+ ChatScrollObserver? chatObserver;
Future<void> loadMessages() async {
...
+ chatObserver?.standby();
+ // or call
+ // chatObserver?.observeSwitchShrinkWrap();
emit(state.copyWith(
status: MessageListStatus.success,
items: _items.values.toList(growable: false),
lastMessageId: 0));
}
...
}
https://github.com/user-attachments/assets/9159abf7-3f22-4fb4-b7c1-74006add5c7d
Here is the video demonstration. When i scroll up (much higher than fixedPositionOffset) and press Add button for the first time, my list jumps, and then after i click Add button more, it stays in place as expected.
Adding standy() or observeSwitchShrinkWrap() to Cubit seems to work fine, but what is better to use in this case? When I call standy method, it has changeCount setted to 1 by default, is it fine in this case?
Also, should I move my ListViewObserver inside BlocBuilder widget, so that it gets rebuild everytime ListView rebuilds too?
Adding standy() or observeSwitchShrinkWrap() to Cubit seems to work fine, but what is better to use in this case? When I call standy method, it has changeCount setted to 1 by default, is it fine in this case?
Which one to use depends on your purpose. observeSwitchShrinkWrap
is only used to switch the shrinkWrap
value.
The standby
includes observeSwitchShrinkWrap
and keeping the scroll position function. In your example, the two are similar because standby
will return in line 121 of the following code.
Also, should I move my ListViewObserver inside BlocBuilder widget, so that it gets rebuild everytime ListView rebuilds too?
Optional, but if you haven’t moved ListViewObserver inside BlocBuilder widget, you’ll need to call observerController.reattach
when the ListView’s BuildContext changes.
Gotcha.
I changed my code a bit to show loading indicator on initial build, and on success build a ListView, and now the jumping on initial list build problem is back again. I updated my repo, here I build loading indicator: https://github.com/nikita488/scrollview_observer_test/blob/master/lib/main.dart#L80
Can this be fixed?
Since your ListView does not always appear, it is recommended that you modify it as follows.
class _MainAppState extends State<MainApp> {
...
late final ChatScrollObserver chatObserver;
+ BuildContext? listViewCtx;
@override
void initState() {
...
}
...
Expanded(
child: ListViewObserver(
controller: observerController,
+ sliverListContexts: () {
+ return [
+ if (listViewCtx != null) listViewCtx!,
+ ];
+ },
child: BlocBuilder<MessageListCubit, MessageListState>(
buildWhen: (previous, current) => current.status.isSuccess,
builder: (context, state) {
final items = state.items;
if (state.status == MessageListStatus.initial) {
return const Center(child: CircularProgressIndicator());
} else {
print('Rebuild List: ${chatObserver.isShrinkWrap}');
Widget resultWidget = ListView.builder(
controller: scrollController,
reverse: true,
physics: ChatObserverClampingScrollPhysics(
observer: chatObserver),
shrinkWrap: chatObserver.isShrinkWrap,
itemCount: items.length,
itemBuilder: (context, index) {
+ if (listViewCtx != context) {
+ listViewCtx = context;
+ observerController.reattach();
+ print('NEEDS REATTACH');
+ // Waiting for reattach to complete
+ WidgetsBinding.instance
+ .addPostFrameCallback((timeStamp) {
+ chatObserver.observeSwitchShrinkWrap();
+ });
+ }
- if (scrollController.hasClients &&
- (observerController.sliverContexts.isEmpty ||
- observerController.sliverContexts.first !=
- context)) {
- observerController.reattach();
- print('NEEDS REATTACH');
- }
final itemIndex = index;
final reversedIndex = items.length - itemIndex - 1;
final item = items[reversedIndex];
return ListTile(
title: Text(item.text),
leading: const Icon(Icons.message));
});
return resultWidget;
}
}),
),
),
...
Thank you very much for your help and explanation, works like a charm now!
Also thank you for your hard work on this package, will use it more in the near future.
Platforms
Android, Web
Description
I want to have a list of items that will keep it's scroll position everytime new item is added or old item is removed.
Initially i don't have list items, so when i open page with this list it fetch data via REST method and wait for response to get initial items list.
But it seems that ChatScrollObserver observeSwitchShrinkWrap tries to observe it when creating ChatScrollObserver object in the initState method, which observes it at the end of the frame, but initial list data might be not loaded at this point.
In this case when i use standby method and add item to list later, it didn't initially keep the scroll position (made a slight jump), but then it switches shrinkWrap state and after that scroll position is keeped when adding more items to list.
How to handle this?
My code
No response
Try do it
No response