flutter即时通讯聊天下拉刷新更多消息思路及代码实现

一、思路回顾
首先,在聊天区域整体是一个可滚动的界面,我们的需求是通过用户下拉手势,即可获取新的聊天记录,并加在原聊天记录的顶端,给用户制造一种无感连接的体验,加载或者加载完毕都要对用户进行信息的提示。

即可以将思路分为以下几步:
1、在有限的高度中,加载二十条聊天记录,形成可以滚动的区域
2、滑动至滚动区域的【顶部】时,调用触发获取新的20条消息的接口,且该过程中顶部有提示文字,“正在加载中…”
3、接口获取数据完毕后,对数据进行必要的处理后,将数据拼接至原数据之中,顶部且有充足的位置留给新给的数据。如果再无新的20条消息,则将提示文字改成“已加载显示全部消息”。

但是以上以一个正常的ListView来实现,便会出现一个问题,当新的数据出现时,将数据通过insertAll方法插入至第0条之前的位置时,便会出现新的数据将旧的数据顶替的效果。至于为什么会出现这一点,我估计是因为我们滑动到顶部时,我们并没有为顶部至第一条消息之间留出一个新的距离,导致滚动距离还停留在顶部,但是数据的后二十条已经变成了新的二十条,所以会出现一个【替换】效果,但是我们需要的是一个拼接效果。

于是我在网络上找到了解决方案(毕竟不是我自己想出来的…),找到的时候还是觉得,还是大家聪明啊。。。。但是我还是想自己再捋一捋整个思路
原文地址:https://cloud.tencent.com/developer/article/1647244

二、正确且最终的实现思路是:
使用Listview,并且flutter的列表滚动还带有了reverse的属性,也就是可以将整个列表进行旋转颠倒,这样我们的底部变成了顶部,顶部其实是列表的底部,在新的数据进行拼接时,就会自动在下面留出一部分距离,也就不会造成替换的效果。
具体思路:
1、当整体历史消息不为空时返回可以滚动的区域,使用ScrollConfiguration包裹Listview,以便实现自己想要的行为。
2、对Listview进行reverse,且itemcount为数据长度+1,留出空位用于显示加载提示信息
3、在数据长度+1 的位置(我们看到的顶部)加载提示信息,其余位置加载消息体
4、对滚动进行监听,当滚动位置大于当前最大滚动距离时,修改加载状态,
5、根据加载状态对加载提示信息进行单独的控制
整个ListView的代码如下:

Widget buildMessageListContainer() {
  if (_historyMessageList.length == 0) {
    return Container(
        height: Adapt.px(50),
        alignment: Alignment.center,
        child: Text(
          '暂无历史消息',
          style: TextStyle(color: Colors.grey, fontSize: Adapt.px(13)),
        ));
  } else {
    return Column(
      children: [
        Expanded(
          child: ScrollConfiguration(
            // color: Colors.green,
            behavior: ChatScrollBehavior(),
            child: ListView.builder(
                controller: _scrollController,
                reverse: true,
                physics: ChatScrollPhysics(
                    parent: AlwaysScrollableScrollPhysics()),
                itemCount: _historyMessageList.length + 1,
                itemBuilder: (BuildContext context, int index) {
                  if (index == _historyMessageList.length) {
                    return loadingView();
                  } else {
                    return getMessageRow(index);
                  }
                }),
          ),
        ),
        SizedBox(
          height: INPUT_CONTAINER_HEIGHT,
        )
      ],
    );
  }
}

ChatScrollPhysics和ChatScrollBehavior是原博主为了优化滚动效果和行为对方法进行了重写。
ChatScrollPhysics:目的是为了实现下滑加载带弹性效果,上滑屏蔽弹性效果
ChatScrollBehavior:使用ScrollConfiguration包裹滑动组件behavior设置成自己实现的behavior。

滚动监听:

void scrollListener() {
  if (_scrollController!.position.pixels >=
      _scrollController!.position.maxScrollExtent) {
    _getMoreData();
  }
}
_scrollController!.addListener(scrollListener);

获取新数据且改变加载提示信息

void _getMoreData() async {
  if (_loadStatus == LoadingStatus.STATUS_IDLE) {
    _loadStatus = LoadingStatus.STATUS_LOADING;
    loadText = '加载中...';
    List moreList = [];
    var sentTime =
        this._historyMessageList[_historyMessageList.length - 1].sentTime;
    var list = await RongImSdk.getHistoryMessage(_groupId, sentTime, 20, 0);
    if (list.length > 0) {
      //去掉第一条,因为第一条已经显示
      for (int i = 1; i < list.length; i++) {
        moreList.add(list[i]);
      }
      if (moreList.length > 0) {
        _loadStatus = LoadingStatus.STATUS_IDLE;
        this._historyMessageList.addAll(moreList);
      } else {
        _loadStatus = LoadingStatus.STATUS_COMPLETED;
        loadText = '已显示全部消息';
      }
    } else {
      _loadStatus = LoadingStatus.STATUS_COMPLETED;
      loadText = '已显示全部消息';
    }
    setState(() {});
  }
}

加载提示信息样式:

Widget loadingView() {
  var loadingTs =
      TextStyle(color: Constants.ITEM_LABEL_COLOR_999, fontSize: 12);
  var loadingText = Padding(
    padding: EdgeInsets.only(left: Adapt.px(0)),
    child: Text(
      loadText,
      style: loadingTs,
    ),
  );
  var loadingIndicator = SizedBox(
    child: CircularProgressIndicator(
        strokeWidth: Adapt.px(2),
        valueColor: AlwaysStoppedAnimation(Colors.blue)),
    width: Adapt.px(12),
    height: Adapt.px(12),
  );
  return Padding(
        padding: EdgeInsets.only(top: Adapt.px(20), bottom: Adapt.px(20)),
        child: Row(
          children: <Widget>[
            _loadStatus == LoadingStatus.STATUS_LOADING
                ? loadingIndicator
                : Text(''),
            loadingText
          ],
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
        ));
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>