键盘隐藏了我的
ListView
(GroupedListView)。我认为这是因为 Expanded
小部件。
我的身体:
Column(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: GroupedListView<dynamic, String>(
controller: _scrollController,
keyboardDismissBehavior:
ScrollViewKeyboardDismissBehavior.onDrag,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
itemBuilder: (context, message) {
return ListTile(
title: ChatBubble(message),
);
},
elements: messages,
groupBy: (message) => DateFormat('MMMM dd,yyyy')
.format(message.timestamp.toDate()),
groupSeparatorBuilder: (String groupByValue) =>
getMiddleChatBubble(context, groupByValue),
itemComparator: (item1, item2) =>
item1.timestamp.compareTo(item2.timestamp),
useStickyGroupSeparators: false,
floatingHeader: false,
order: GroupedListOrder.ASC,
),
),
),
WriteMessageBox(
group: group,
groupId: docs[0].id,
tokens: [widget.friendToken])
],
);
为什么
resizeToAvoidBottomInset
不起作用?
我已经向 Flutter 团队提出了 问题
简而言之:使用
reversed: true
。
您所看到的是预期行为,原因如下:
当屏幕上的某些内容调整大小时,ListView
会保留其滚动偏移量。此偏移量是列表从开头滚动到的像素数。默认情况下,从顶部开始计数,列表增长到底部。
如果使用
reversed: true
,则滚动位置从底部开始计算,因此最底部的位置是0
,列表从下到上增长。它有很多好处:
0
的最底部位置将被保留。任何其他位置也是如此。在任何位置,列表都会移到顶部,最后一个可见元素仍然是最后一个可见元素。
从数据库获取消息后,对消息进行排序和分页会更容易。您只需按日期时间降序排序并附加到列表中,无需在将对象列表提供给 ListView 之前反转对象列表。
它只适用于没有监听器和控制器操作的情况。一般来说,声明式解决方案更可靠。
经验法则是反转分页列表,在顶部加载更多项目。
这是例子:
import 'package:flutter/material.dart';
void main() async {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: 30,
reverse: true,
itemBuilder: (context, i) => ListTile(title: Text('Item $i')),
),
),
const TextField(),
],
),
),
);
}
}
至于
resizeToAvoidBottomInset
,它完成了它的工作。键盘打开时,Scaffold
确实缩短了。 ListView
也是如此。因此它显示的项目较少。对于非反转列表,最底部的已经消失了。
看起来您希望 GroupedListView 从最后一行开始可见。 WriteMessageBox 被键盘推起并遮盖了最后的消息。最直接的解决方案是当键盘可见时将列表滚动到底部。也就是说,当 WriteMessageBox 获得焦点时。
在 build() 方法中向 WriteMessageBox 添加 FocusScope。就变成了
FocusScope(
child: Focus(
child: WriteMessageBox(),
onFocusChange: (focused) {
if (focused) {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
)
)
您可以使用
MediaQueryData
获取键盘的高度,然后将 ListView
向上滚动该数字。
创建这个类:
class HandleScrollWidget extends StatefulWidget {
final BuildContext context;
final Widget child;
final ScrollController controller;
HandleScrollWidget(this.context, {required this.controller, required this.child});
@override
_HandleScrollWidgetState createState() => _HandleScrollWidgetState();
}
class _HandleScrollWidgetState extends State<HandleScrollWidget> {
double? _offset;
@override
Widget build(BuildContext context) {
final bottom = MediaQuery.of(widget.context).viewInsets.bottom;
if (bottom == 0) {
_offset = null;
} else if (bottom != 0 && _offset == null) {
_offset = widget.controller.offset;
}
if (bottom > 0) widget.controller.jumpTo(_offset! + bottom);
return widget.child;
}
}
用途:
final ScrollController _controller = ScrollController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('ListView')),
body: HandleScrollWidget(
context,
controller: _controller,
child: Column(
children: [
Expanded(
child: ListView.builder(
controller: _controller,
itemCount: 100,
itemBuilder: (_, i) => ListTile(title: Text('Messages #$i')),
),
),
TextField(decoration: InputDecoration(hintText: 'Write a message')),
],
),
),
);
}
您似乎正在使用文本字段,因此它会隐藏数据,或者有时可能会通过黑色和黄色条纹溢出边框
更好地使用
SingleChildScrollView
,对于滚动方向,使用 scrollDirection
和参数 Axis.vertical
或 Axis.horizontal
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child :Column(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: GroupedListView<dynamic, String>(
controller: _scrollController,
keyboardDismissBehavior:
ScrollViewKeyboardDismissBehavior.onDrag,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
itemBuilder: (context, message) {
return ListTile(
title: ChatBubble(message),
);
},
elements: messages,
groupBy: (message) => DateFormat('MMMM dd,yyyy')
.format(message.timestamp.toDate()),
groupSeparatorBuilder: (String groupByValue) =>
getMiddleChatBubble(context, groupByValue),
itemComparator: (item1, item2) =>
item1.timestamp.compareTo(item2.timestamp),
useStickyGroupSeparators: false,
floatingHeader: false,
order: GroupedListOrder.ASC,
),
),
),
WriteMessageBox(
group: group,
groupId: docs[0].id,
tokens: [widget.friendToken])
],
);
);
请尝试此解决方案。希望它对你有用。谢谢。
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: GroupedListView<dynamic, String>(
scrollDirection: Axis.vertical,
shrinkWrap: true,
controller: _scrollController,
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
itemBuilder: (context, message) {
return ListTile(
title: ChatBubble(message),
);
},
elements: messages,
groupBy: (message) =>
DateFormat('MMMM dd,yyyy').format(message.timestamp.toDate()),
groupSeparatorBuilder: (String groupByValue) =>
getMiddleChatBubble(context, groupByValue),
itemComparator: (item1, item2) =>
item1.timestamp.compareTo(item2.timestamp),
useStickyGroupSeparators: false,
floatingHeader: false,
order: GroupedListOrder.ASC,
),
),
),
WriteMessageBox(
group: group, groupId: docs[0].id, tokens: [widget.friendToken])
简而言之:使用reverse: true,将滚动位置跳至0。
final scrollController = ScrollController();
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (scrollController.hasClients) {
scrollController.jumpTo(scrollController.position.maxScrollExtent);
}
});
}
Widget _buildScrollView(){
return SingleChildScrollView(
reverse: true,
controller: scrollController,
child: [....],
);
}
await Future.delayed(const Duration(毫秒: 100)); 滚动之前