robert-luoqing / flutter_list_view

MIT License
47 stars 17 forks source link

initIndex only works for 1st and last index #35

Closed shahbazhashmi closed 4 months ago

shahbazhashmi commented 5 months ago

I am trying to put dynamic initIndex in the list view but it just picks random index. Although it works perfectly with 0 and n -1 index. This my code

FlutterListView(
                          controller: chatListScrollController,
                          shrinkWrap: true,
                          delegate: FlutterListViewDelegate(
                            (final BuildContext _, final int index) {
                              final item = chatList[index];
                              return ChatMessageWidget(
                                image: item.sender?.profilePicture,
                                message: item.text,
                                isSelf: item.isMyChat(myUserId),
                              );
                            },
                            onItemKey: (final index) => chatList[index].id,
                            childCount: chatList.length,
                            initIndex: chatListInitialIndex,
                            disableCacheItems: true,
                          ),
                        )
robert-luoqing commented 5 months ago

I can't reproduce your issue I have made a sample with your code snippet. But it works fine. The code is here. https://github.com/robert-luoqing/flutter_list_view/blob/master/example/lib/init_jump2.dart

Could you please rewrite the code, reproduce the issue, and return it to me? I can fix it if it is a bug.

shahbazhashmi commented 5 months ago

Here is the code. To avoid boilerplate I used hooks

class TestApiWidget extends HookWidget {
  TestApiWidget({super.key});

  @override
  Widget build(final BuildContext context) {
    final chatListScrollController = useMemoized(FlutterListViewController.new);
    final dataList = useState(List.generate(50, (final index) => index + 1));

    void scrollToEdgeListener() {
      if (chatListScrollController.position.atEdge) {
        final isTop = chatListScrollController.position.pixels == 0;
        if (isTop) {
          /// At the top
        } else {
          /// At the bottom
          dataList.value = List.generate(dataList.value.length + 50, (final index) => index + 1);
        }
      }
    }

    useEffect(
          () {
        if (dataList.value.isNotEmpty) {
          chatListScrollController.addListener(scrollToEdgeListener);
        }
        return () {
          chatListScrollController.removeListener(scrollToEdgeListener);
        };
      },
      [dataList.value.isNotEmpty],
    );

    return FlutterListView(
      controller: chatListScrollController,
      shrinkWrap: true,
      delegate: FlutterListViewDelegate(
            (final BuildContext _, final int index) {
          final item = dataList.value[index];
          return Container(
            margin: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
            child: Text(item.toString()),
          );
        },
        onItemKey: (final index) => dataList.value[index].toString(),
        childCount: dataList.value.length,
        initIndex: 10,
        disableCacheItems: true,
      ),
    );
  }
}

Here I reinitialise dataList (which rebuilds widget) and add 50 more items on scroll to bottom. initIndex works only once not on further rebuilds. It should support dynamic initIndex.

robert-luoqing commented 4 months ago

It works fine when I test. Please reference the screen record. It jump to 10 in the first loads. I think there is some gap between us. For initIndex property, The defined as below

initIndex: It only works once when FlutterListView loads data for the first time. If you want to jump to the index after the data change. you can invoke chatListScrollController.sliverController.jumpToIndex(50); after data changed

What behavior you want in the list? you want to reverse and keep position when load data?

More chat example, please ref https://github.com/robert-luoqing/flutter_list_view/blob/master/example/lib/chat.dart https://github.com/robert-luoqing/flutter_list_view/blob/master/example/lib/chat2.dart

Reference code https://github.com/robert-luoqing/flutter_list_view/blob/master/example/lib/init_jump2.dart

void scrollToEdgeListener() {
      if (chatListScrollController.position.atEdge) {
        final isTop = chatListScrollController.position.pixels == 0;
        if (isTop) {
          /// At the top
        } else {
          /// At the bottom
          dataList.value = List.generate(
              dataList.value.length + 50, (final index) => index + 1);
          // Jump to 50.
          WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
            chatListScrollController.sliverController.jumpToIndex(50);
          });
        }
      }
    }

https://github.com/robert-luoqing/flutter_list_view/assets/13658054/8dac1324-d51e-4905-8d6d-26787d18bd44

shahbazhashmi commented 4 months ago

I send list of chats and initial index from a cubit. I create this initial index based on data. If it has older data I send this initial index : old data length - 1. I fetch old data when user scroll to top.

So the logic is simple, when we get new data initial index should be n-1. When we get old data it should be index of first chat in previous list - 1.

The logic to calculate initial index works fine. But I am not able to use it correctly in the widget.

robert-luoqing commented 4 months ago

About your logic in chat, I think you need refer https://github.com/robert-luoqing/flutter_list_view/blob/master/example/lib/chat.dart

It is specify for chat, The logic is:

  1. Set reverse to true (ListView also have the property)
  2. Reverse chat data. for example, The latest message should be first item of data.
  3. Insert new data in front of old data
FlutterListView(
              reverse: true,
              controller: listViewController,
              delegate: FlutterListViewDelegate(
                  (BuildContext context, int index) => _renderItem(index),
                  childCount: messages.length,
                  onItemKey: (index) => messages[index].id.toString(),
                  keepPosition: true,
                  keepPositionOffset: 40,
                  initIndex: initIndex,
                  initOffset: initOffset,
                  initOffsetBasedOnBottom: initOffsetBasedOnBottom,
                  forceToExecuteInitIndex: forceToExecuteInitIndex,
                  firstItemAlign: FirstItemAlign.end))),

The user case for initIndex is that you want jump to latest unread message when load your messages. It is not to use initIndex to keep scroll to last message.

robert-luoqing commented 4 months ago

I have made a simple chat. please ref https://github.com/robert-luoqing/flutter_list_view/blob/master/example/lib/simple_chat.dart

The code have below logic:

  1. Load messages in initState
  2. Click button to mock up receive new messages
  3. Send message when input message and click "Send"

Notice: keepPosition: true, It will cause that No any scroll down when new message coming and scroll offset is above 40. The case is when user read old message, He/She don't want be disturb when new message coming.

If you want more advance feature. You can read chat.dart and chat2.dart.

Hope useful for you.