The chat list is a very detailed scenario. In the previous article on Flutter, a CustomScrollView and its center were used to solve the problem of Flutter when data was updated. But then another question was raised:

As shown in the following GIF, although the list does not jump after adding new data, if the list is long enough, there will be a blank at the top.

As shown in the code below, the cause of this problem is the addition of center to solve the beat problem, because the list is reverse, and the red SliverList is only three pieces long, which is not high enough to leave a blank at the top.

CustomScrollView(
        controller: scroller,
        reverse: true,
        center: centerKey,
        slivers: [
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                var item = newData[index];
                if (item.type == "Right")
                  return renderRightItem(item);
                else
                  return renderLeftItem(item);
              },
              childCount: newData.length,
            ),
          ),
          SliverPadding(
            padding: EdgeInsets.zero,
            key: centerKey,
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                var item = loadMoreData[index];
                if (item.type == "Right")
                  return renderRightItem(item);
                else
                  return renderLeftItem(item);
              },
              childCount: loadMoreData.length,
            ),
          ),
        ],
      )


Copy the code

The following picture combined with the picture to understand more image:

  • centerIt’s actually the starting anchor point of the list, and we gave the anchor pointSliverPaddingAnd because the list isreverseSo the starting position is at the bottom of the screen;
  • Old data in redSliverListIn the code is incenterAnd becausereverseSo it’s actually the yellow part;
  • So even though it’s greenSliverListAlthough the data has been added, it has beencenterThere’s not enough height up there, so you get yellowSliverListWhite space at the top;

Combined with this problem, it can be found that the key point lies in reverse. By comparing the chat list requirements of wechat and QQ, when there is no data, the message data should start from the top, so we need to adjust the list implementation by referring to the implementation mode of wechat /QQ.

Therefore, the following code is optimized for the new interaction scenario:

  • Get rid ofCustomScrollViewreverse
  • Switch the twoSliverListThe location to load old dataSliverListIn thecenterIn front of;
CustomScrollView(
  controller: scroller,
  center: centerKey,
  slivers: [
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          var item = loadMoreData[index];
          if (item.type == "Right")
            return renderRightItem(item);
          else
            return renderLeftItem(item);
        },
        childCount: loadMoreData.length,
      ),
    ),
    SliverPadding(
      padding: EdgeInsets.zero,
      key: centerKey,
    ),
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          var item = newData[index];
          if (item.type == "Right")
            return renderRightItem(item);
          else
            return renderLeftItem(item);
        },
        childCount: newData.length,
      ),
    ),
  ],
)
Copy the code

Isn’t that easy? Is that it? After running, it is also shown in the figure below. It can be seen that there is no blank space in the code after running, no new data jumping, and bidirectional sliding is normal. Do you know why?

As shown in the picture below, the adjustment is structurally changed to the logic on the right:

  • The data start anchor is at the top of the page, so there is no problem with a blank top;
  • incenterThe followingSliverListNormal display in forward order, used to display new data;
  • incenterThe aboveSliverListThe list will be changed tocenterIs the starting point in reverse order display, used to load old data;

Of course, one thing to note here is that the first page of data loaded when the start comes in should be a green forward SliverList, because the start point is at the top. If you don’t use the green forward SliverList below, the first data will not be seen.

At this point, one might say, if the scenario shown below is loaded with only old data and no new data, the bottom will be empty.

Yes, we actually transferred the problem of leaving the top blank to the bottom, but this problem is not true in actual business scenarios. To enter the chat list, you need to load a full page of data first, so:

  • If there is not enough old data in the first place, such as 3 in the example, there is no scenario to load more old data, so there is no sliding;
  • If old data is enough, the default is enough to fill the list;

As more new data is added, the page fills up so that it can slide and fill up normally, so this implementation makes more sense.

So one might say, right there? What other tips can you optimize? For example, add whether the list is at the bottom or whether to swipe to the latest news when receiving new data.

To realize the optimization is also very simple, first of all, we can be nested NotificationListener, here we mainly for notification. The metrics. ExtentAfter this parameter.

NotificationListener(
  onNotification: (notification) {
    if (notification is ScrollNotification) {
      if (notification.metrics is PageMetrics) {
        return false;
      }
      if (notification.metrics is FixedScrollMetrics) {
        if (notification.metrics.axisDirection == AxisDirection.left ||
            notification.metrics.axisDirection == AxisDirection.right) {
          return false; }}///I get to this value
      extentAfter = notification.metrics.extentAfter;
    }
    return false; },)Copy the code

The if judgment here is only used to avoid the influence of other controls, such as PageView or TextFiled in the list.

So what does the extentAfter parameter do? In fact, there are extentBefore, extentInside, and extentAfter parameters in FixedScrollMetrics, and their relationship is similar to that shown below:

In general:

  • extentInsideThat’s the size of the view window;
  • extentBeforeThat’s how far you can slide in front;
  • extentAfterThat’s how far you can slide back;

Therefore, we only need to determine whether the extentAfter is 0 to determine whether the list is at the bottom, so that different business logic can be applied to the scenario first. For example, as shown in the following figure, according to whether the list is at the bottom, whether to directly jump to the latest data when receiving new data, or to pop up a prompt for the user to click jump.

if (extentAfter == 0) {
  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
    content: Text("You are currently at the bottom, automatically jump to new message item."),
    duration: Duration(milliseconds: 1000))); Future.delayed(Duration(milliseconds: 200), () {
    scroller.jumpTo(scroller.position.maxScrollExtent);
  });
} else {
  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
    content: InkWell(
      onTap: () {
        scroller.jumpTo(scroller.position.maxScrollExtent);
      },
      child:Text("Click on me to automatically jump to new message item.")
    ),
    duration: Duration(milliseconds: 1000))); }Copy the code

So from the chat list scenario, it is not difficult to implement a chat list, but the details that need to be optimized can be many, if you have any questions in this area, please feel free to comment.

Example code visible: github.com/CarGuo/gsy_…