Flutter 实现完美的双向聊天列表效果,滑动列表的知识点

本文将通过一个需求场景,介绍一个非常实用的 Flutter 列表滑动知识点,该问题来源于网友的咨询

如何在 Flutter 上实现一个聊天列表,相信大家都不会觉得有什么困难,不就是一个 ListView ,然后根据类型显示渲染数据吗?这有什么困难的?

理论上确实没什么问题,但是有一个需求场景,却会出现一个无法修复的问题,那就是:聊天列表需要双向插入数据

双向插入数据会导致 ListView 什么问题? 举个例子,首先我们使用常见的 ListView 绘制出一个模拟聊天列表,这里使用了 reverse 反转列表满足 UI 需求,让列表从底部开始网上布局滑动:

ListView.builder(
        controller: scroller,
        reverse: true,
        itemBuilder: (context, index) {
          var item = data[index];
          if (item.type == "Right")
            return renderRightItem(item);
          else
            return renderLeftItem(item);
        },
        itemCount: data.length,
      )

运行后效果如下图所示:

  • 首先添加红色的,模拟加载旧数据 list.add ,可以看到上面的数据出现了,没有问题;
  • 接着我们滑动一段距离,没有问题;
  • 接着添加绿色数据,模拟新收到新消息 list.insert可以看到列表出现了跳动,没有停留在我们之前滑动的位置
  • 我们继续滑动,模拟新收到新消息,列表继续出现跳动;

有问题没有?如果这个效果产品可以接受,那就没问题。但是如果产品拿着 QQ 聊天问你,为什么别人收到新消息,列表不会跳动?这问题不就来了吗~

首先分析问题,为什么列表会出现跳动?在 《不一样角度带你了解 Flutter 中的滑动列表实现》 我们讲过,Flutter 的滑动列表效果主要有三部分组成:

  • Viewport : 它提供的是一个“视窗”的作用,也就是列表所在的可视区域大小;
  • Scrollable :它主要通过对手势的处理来实现滑动效果;
  • Sliver : 准确来说应该是 RenderSliver, 它主要是用于在 Viewport 里面布局和渲染内容,比如 SliverList

也许这些看着太抽象,结合下图:

  • 绿色的 Viewport 就是我们看到的列表窗口大小;
  • 紫色部分就是处理手势的 Scrollable,让黄色部分 SliverListViewport 里产生滑动;
  • 黄色的部分就是 SliverList , 当我们滑动时其实就是它在 Viewport 里的位置发生了变化;

本来一切正常,但是当我们通过 insert 添加绿色部分的数据时,插入头部的数据就会
(绿色部分),就会把原本的 SliverList 数据往后顶上去,从而产生了 SliverList 的位置发现变化。

所以本质上是 SliverList 变长了,起点变了,从而在 Viewport 里的位置发生了变化

那如何去解决这个问题呢?有人可能就会说,那我们让他 jump 回原来的位置不就行了吗?

如下图所示,我们通过记录原本位置,然后添加数据,之后得到添加数据的大小,之后 jump 到原来的位置,效果就是会出现闪动~

所以如何解决这个问题呢?这就涉及到 Flutter 列表滑动的一个关键知识点:center

什么是列表的 center

其实在 centerViewPort 里的一个关键参数,默认是第一个RenderSliver,决定了 scrollOffset = 0 的位置

另外 center 是一个 Key对象, 也就是除了默认之外,我们可以通过 Key 来指定我们想要的 center 位置。

也就是,如果我们旧数据插入到 center 之前,新数据插入到 center 之后,那岂不是列表就不会发现滑动了?

那我们如何配置 center这时候就需要使用到 CustomScrollViewCustomScrollView 支持配置 center, 另外对于 CustomScrollView 是直接配置你需要的 slivers 数组。

也就是说,不像 ListView 那样只有一个 SliverList,我们可以直接配置两个 SliverList,然后按照上面的思路,中间放一个 center

如下面代码所示,因为聊天列表的场景,我们的列表是 reverse 的,所以需要将新数据的 SliverList 放在 centerKey 的上面,把旧数据的 SliverList放在 centerKey 下面。

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,
            ),
          ),
        ],
      )

运行后效果如图所示,可以看到即使在绿色数据新增的时候,列表也没有发生跳转,其实现在的布局滑动效果,就是从原本的 0 ~ xxx 的滑动范围,变成了 -AAA ~ BB 这样的滑动范围。

前面我们说过 center 决定了 scrollOffset = 0 的位置,所以当我们如上面那样布局后,就等于有了从 0 ~ ♾️ 和从 -♾️ ~ 0 的范围,所以当我们 insert 数据到头部时,其实是往 minScrollExtent 的方向插入数据,增加的是负数的 Offset,从而不会导致列表产生位移。

虽然实现很简单,但是如果不去对 Flutter 的滑动列表机制有所了解,就很容易对着 ListvView 陷入僵局,这篇文章也是为了给大家打开思路,提高对 ViewPortSliver 的了解。

如果你对 Flutter 还有什么疑问或者想法,欢迎留言交流~。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355

推荐阅读更多精彩内容

  • ![Flask](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAW...
    极客学院Wiki阅读 7,249评论 0 3
  • 不知不觉易趣客已经在路上走了快一年了,感觉也该让更多朋友认识知道易趣客,所以就谢了这篇简介,已做创业记事。 易趣客...
    Physher阅读 3,420评论 1 2
  • 双胎妊娠有家族遗传倾向,随母系遗传。有研究表明,如果孕妇本人是双胎之一,她生双胎的机率为1/58;若孕妇的父亲或母...
    邺水芙蓉hibiscus阅读 3,702评论 0 2
  • 晴天,拥抱阳光,拥抱你。雨天,想念雨滴,想念你。 我可以喜欢你吗可以啊 我还可以喜欢你吗可以,可是你要知道我们不可...
    露薇霜凝阅读 1,216评论 1 2