flutter - 如何将粘性标题添加到列表视图?
问题描述
我想在不使用第三方库的情况下将粘性标题添加到列表视图。我已经为. grouped list view
_ )? 有没有更好的方法来做到这一点?stack
list view
widget
class GroupedListView<E, G extends Comparable<Object>> extends StatefulWidget {
const GroupedListView({
@required this.groupBy,
@required this.groupBuilder,
@required this.itemBuilder,
Key key,
this.elements,
}) : super(key: key);
final G Function(E element) groupBy;
final Widget Function(G group) groupBuilder;
final Widget Function(BuildContext context, E element) itemBuilder;
final List<E> elements;
@override
_GroupedListViewState<E, G> createState() => _GroupedListViewState<E, G>();
}
class _GroupedListViewState<E, G extends Comparable<Object>>
extends State<GroupedListView<E, G>> {
List<E> _elements;
@override
void initState() {
super.initState();
_elements = widget.elements;
}
@override
Widget build(BuildContext context) => Stack(
children: <Widget>[
ListView.builder(
itemCount: _elements.length * 2,
itemBuilder: (BuildContext context, int index) {
final int actualIndex = index ~/ 2;
if (index.isEven) {
final G currentGroup = widget.groupBy(_elements[actualIndex]);
final G previousGroup = actualIndex - 1 < 0
? null
: widget.groupBy(_elements[actualIndex - 1]);
if (previousGroup != currentGroup) {
return widget.groupBuilder(currentGroup);
}
return Container();
}
return widget.itemBuilder(context, _elements[actualIndex]);
},
),
// THIS WIDGET SHOULD REBUILD BY LISTENING TO SCROLL VIEW CONTROLLER
Container(
color: Colors.black,
child: ListTile(
title: Text(
'STICKY HEADER',
style: TextStyle(color: Colors.white),
),
),
),
],
);
}
示例代码:
class HomePage extends StatefulWidget {
HomePage({
Key key,
this.title,
}) : super(key: key);
final String title;
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: GroupedListView<dynamic, String>(
elements: listItems,
groupBy: (dynamic element) => element['group'],
groupBuilder: (String value) => Container(
color: Colors.grey,
child: ListTile(
title: Text(
value,
style: TextStyle(color: Colors.white),
),
),
),
itemBuilder: (BuildContext context, dynamic element) => ListTile(
title: Text(element['name']),
),
),
);
}
}
final List<Map<String, String>> listItems = <Map<String, String>>[
<String, String>{'name': 'user_01', 'group': 'group_01'},
<String, String>{'name': 'user_02', 'group': 'group_01'},
<String, String>{'name': 'user_03', 'group': 'group_02'},
<String, String>{'name': 'user_04', 'group': 'group_02'},
<String, String>{'name': 'user_05', 'group': 'group_02'},
<String, String>{'name': 'user_06', 'group': 'group_03'},
<String, String>{'name': 'user_07', 'group': 'group_04'},
<String, String>{'name': 'user_08', 'group': 'group_04'},
<String, String>{'name': 'user_09', 'group': 'group_05'},
<String, String>{'name': 'user_10', 'group': 'group_05'},
<String, String>{'name': 'user_11', 'group': 'group_06'},
<String, String>{'name': 'user_12', 'group': 'group_06'},
];
任何帮助将不胜感激。
解决方案
是的,您可以使用 aStack
来实现此行为。首先,您需要做一些数学运算来获得每个组标题行的高度。然后你需要给滚动控制器添加一个监听器。此侦听器检查滚动控制器的当前偏移量是否在与组相关的区域中,它将current
变量设置为组名列表中该组的索引。separatorHeight
是组标题行的高度。tileHeight
是项目行的高度。
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
class GroupedListView<E, G extends Comparable<Object>> extends StatefulWidget {
const GroupedListView({
@required this.groupBy,
@required this.groupBuilder,
@required this.itemBuilder,
@required this.separatorHeight,
@required this.tileHeight,
Key key,
this.elements,
}) : super(key: key);
final G Function(E element) groupBy;
final Widget Function(G group) groupBuilder;
final Widget Function(BuildContext context, E element) itemBuilder;
final List<E> elements;
final double separatorHeight;
final double tileHeight;
@override
_GroupedListViewState<E, G> createState() => _GroupedListViewState<E, G>();
}
class _GroupedListViewState<E, G extends Comparable<Object>>
extends State<GroupedListView<E, G>> {
List<E> _elements;
List<double> _groupHeights;
List<G> _groupNames;
int _currentGroupIndex = 0;
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
_elements = widget.elements;
_groupNames = groupBy<E, G>(_elements, widget.groupBy)
.entries
.map<G>((dynamic entry) => entry.key)
.toList();
_groupHeights = groupBy<E, G>(_elements, widget.groupBy)
.entries
.map<double>((dynamic entry) => entry.value.length.toDouble())
.toList();
double sum = 0;
_groupHeights = _groupHeights
.map<double>((double itemCount) =>
sum += itemCount * widget.tileHeight + widget.separatorHeight)
.toList();
_scrollController.addListener(() {
final double controllerOffset =
_scrollController.offset + widget.separatorHeight;
if (controllerOffset < _groupHeights.first) {
setState(() => _current = 0);
} else {
for (int i = 1; i < _groupHeights.length; i++) {
if (controllerOffset >= _groupHeights[i - 1] &&
controllerOffset < _groupHeights[i]) {
setState(() => _current = i);
break;
}
}
}
});
}
@override
Widget build(BuildContext context) => Stack(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: widget.separatorHeight),
),
ListView.builder(
key: widget.key,
controller: _scrollController,
itemCount: _elements.length * 2,
itemBuilder: (BuildContext context, int index) {
final int actualIndex = index ~/ 2;
if (index.isEven) {
final G currentGroup = widget.groupBy(_elements[actualIndex]);
final G previousGroup = actualIndex - 1 < 0
? null
: widget.groupBy(_elements[actualIndex - 1]);
if (previousGroup != currentGroup) {
return SizedBox(
height: widget.separatorHeight,
child: widget.groupBuilder(currentGroup),
);
}
return Container();
}
return SizedBox(
height: widget.tileHeight,
child: widget.itemBuilder(context, _elements[actualIndex]),
);
},
),
SizedBox(
height: widget.separatorHeight,
child: widget.groupBuilder(_groupNames[_currentGroupIndex]),
),
],
);
}
结果:
推荐阅读
- php - 为什么我的错误处理程序没有报告 PHP max_input_vars 警告?
- visual-studio-2019 - VS2019 - 组合键(Ctrl+K、Ctrl+C)绑定到当前不可用的命令(评论选择)
- azure - Microsoft Azure Application Insights 中的“事件时间”代表什么?
- python - 无法使用python根据BQ中的查询创建另一个表
- serverless - serverless.cli.consoleLog 在无服务器更新后引发错误 - 收到拒绝:TypeError:无法读取未定义的属性“consoleLog”
- kubernetes - 如何使用负载均衡器类型为 aws eks 提供弹性 IP 以进行外部服务?
- javascript - 带条纹的赛普拉斯:未加载元素高度
- python - Botframework 发送主动消息然后字符串不为空
- linux - 安装 deb 包时,出现错误:gtk-update-icon-cache: No theme index file。嘿!
- azure-devops-server-2019 - 将 Azure Devops Server 2019 升级到 Azure Devops Server 2020