在常见社交App中,发送消息或者接收到他人消息后,消息列表都会自动滑动到底部,不需要我们手动滑动,这样的用户体验好。
ListView使用ScrollController来控制滑动,其中有jumpTo
、animateTo
2个方法滑动到指定的位置。
/// 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
能设置滑动动画。
第一个参数value
和offset
都是代表ListView中的指定位置。
那么应该传入哪个值才是代表ListView的底部呢?
通过ScrollController
2个方法的源码发现最终调用的是_position
的jumpTo
、animateTo
方法。
_postion是一个ScrollPostion
,ScrollPostion
中有2个变量minScrollExtent
、maxScrollExtent
,分别代表滑动到顶部、底部的位置。
最终可以使用以下代码实现滑动到底部
scrollController.jumpTo(scrollController.position.maxScrollExtent);
首先我们来定义一个简单的聊天界面。一个ListView,一个输入框,一个发送消息的按钮。
/// 消息列表
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!大功告成,下面是演示效果。