Flutter之ListView实现自动滑动到底部

文章目录

  • 使用场景
  • 思路
    • ScrollPostion
  • 实现
    • 布局
    • 滑动到底部
      • 问题
      • 解决方案

使用场景

在常见社交App中,发送消息或者接收到他人消息后,消息列表都会自动滑动到底部,不需要我们手动滑动,这样的用户体验好。

思路

ListView使用ScrollController来控制滑动,其中有jumpToanimateTo2个方法滑动到指定的位置。

/// packages/flutter/lib/src/widgets/scroll_controller.dart

void jumpTo(double value){
    assert(_positions.isNotEmpty, 'ScrollController not attached to any scroll views.');
    for (final ScrollPosition position in List.from(_positions))
      position.jumpTo(value);
}

Future animateTo(
    double offset, {
      required Duration duration,
      required Curve curve,
    }) async {
    assert(_positions.isNotEmpty, 'ScrollController not attached to any scroll views.');
    await Future.wait(>[
      for (int i = 0; i < _positions.length; i += 1) _positions[i].animateTo(offset, duration: duration, curve: curve),
    ]);
}

看命名就能看出来,jumpTo不带动画效果,animateTo能设置滑动动画。

第一个参数valueoffset都是代表ListView中的指定位置。

ScrollPostion

那么应该传入哪个值才是代表ListView的底部呢?

通过ScrollController2个方法的源码发现最终调用的是_positionjumpToanimateTo方法。

_postion是一个ScrollPostionScrollPostion中有2个变量minScrollExtentmaxScrollExtent,分别代表滑动到顶部、底部的位置。

最终可以使用以下代码实现滑动到底部

scrollController.jumpTo(scrollController.position.maxScrollExtent);

实现

布局

首先我们来定义一个简单的聊天界面。一个ListView,一个输入框,一个发送消息的按钮。

Flutter之ListView实现自动滑动到底部_第1张图片

/// 消息列表
List _messages = ["在干嘛","吃饭了吗","想你了","早点睡"];
TextEditingController _textEditingController = TextEditingController();
ScrollController _scrollController = ScrollController();

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('女神'),
    ),
    body: Column(
      children: [
        Expanded(
          child: ListView.builder(
            controller: _scrollController,
            itemBuilder: (context, index) {
              return SizedBox(
                child: Center(child: Text(_messages[index])),
                height: 50,
              );
            },
            itemCount: _messages.length,
          ),
        ),
        Row(
          children: [
            Expanded(
              child: Padding(
                padding: const EdgeInsets.symmetric(horizontal: 10),
                child: TextField(
                  controller: _textEditingController,
                  decoration: InputDecoration(
                    hintText: "请输入消息",
                  ),
                  textInputAction: TextInputAction.send,
                  onSubmitted: sendMessage,
                ),
              ),
            ),
            IconButton(
              onPressed: () {
                /// 发送消息
                sendMessage(_textEditingController.text.trim());
                /// 清除输入框中的内容
                _textEditingController.text = "";
              },
              icon: Icon(Icons.send),
            ),
          ],
        ),
        SizedBox(height: 5),
      ],
    ),
  );
}

滑动到底部

点击发送按钮时,获取输入框中的消息,添加到消息列表中,更新ui,然后滑动到底部。

void sendMessage(String message) {
  if (message.isEmpty) return;
  setState(() {
    _messages.add(message);
  });
  _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}

问题

看起来没啥问题,但是运行发现,每次发送完消息后,列表并没有滑动到底部,而是滑动到了倒数第二次数据。

原因是执行滑动到底部的操作时,列表数据还没有更新,maxScrollExtent还是更新之前的值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pucOH0Q3-1631608253352)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/308f11e872644472b83f78760105e2f3~tplv-k3u1fbpfcp-watermark.image)]

解决方案

那么我们需要等待setState刷新列表之后,再执行滑动操作。
所以我们可以使用延迟执行方案。一般延迟时间设置300到500毫秒即可。

void sendMessage(String message) {
  if (message.isEmpty) return;
  setState(() {
    _messages.add(message);
  });
  /// 延迟500毫秒,再进行滑动
  Future.delayed(Duration(milliseconds: 500), () {
    _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
  });
}

ok!大功告成,下面是演示效果。

你可能感兴趣的:(Flutter学习,flutter,listview,Scroll,dart)