Flutter resizeToAvoidBottomInset true 不适用于 Expanded ListView
Flutter resizeToAvoidBottomInset true not working with Expanded ListView
键盘隐藏了我的 ListView
(GroupedListView)。我认为这是因为 Expanded
小部件。
我的body:
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
不工作?
您使用的文本字段似乎隐藏了数据,或者有时它可能会溢出黑色和黄色条纹的边框
最好使用 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])
您似乎希望 GroupedListView 从最后一行可见。 WriteMessageBox 被键盘向上推,遮盖了最后的消息。最直接的解决方案是在键盘可见时将列表滚动到底部。也就是说,当 WriteMessageBox 获得焦点时。
在 build() 方法中将 FocusScope 添加到 WriteMessageBox。变成
FocusScope(
child: Focus(
child: WriteMessageBox(),
onFocusChange: (focused) {
if (focused) {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
)
)
截图:
代码:
您可以使用 MediaQueryData
获取键盘的高度,然后将 ListView
向上滚动该数字。
创建此 class:
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')),
],
),
),
);
}
简而言之:使用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
也是。所以它显示你更少的项目。对于非反向列表,最底部已经消失了。
键盘隐藏了我的 ListView
(GroupedListView)。我认为这是因为 Expanded
小部件。
我的body:
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
不工作?
您使用的文本字段似乎隐藏了数据,或者有时它可能会溢出黑色和黄色条纹的边框
最好使用 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])
您似乎希望 GroupedListView 从最后一行可见。 WriteMessageBox 被键盘向上推,遮盖了最后的消息。最直接的解决方案是在键盘可见时将列表滚动到底部。也就是说,当 WriteMessageBox 获得焦点时。
在 build() 方法中将 FocusScope 添加到 WriteMessageBox。变成
FocusScope(
child: Focus(
child: WriteMessageBox(),
onFocusChange: (focused) {
if (focused) {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
)
)
截图:
代码:
您可以使用 MediaQueryData
获取键盘的高度,然后将 ListView
向上滚动该数字。
创建此 class:
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')),
],
),
),
);
}
简而言之:使用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
也是。所以它显示你更少的项目。对于非反向列表,最底部已经消失了。