我一直在尝试获取按日期排序的一些消息,但没有成功。我尝试过不同的软件包,例如 grouped_list 和 sticky_headers。
使用 sticky_headers 我设法在每个帖子上方都有一个标题,但这不是我想要的。我想按天对邮件进行排序。就像下图一样:
下面我有一个数据集,其中包含我从 API 获取的数据。我的目的是从 API 获取 40 条消息。这 40 条消息按时间排序,只有消息必须在应用程序中按天分组,如上图所示。
编辑: 在 flutter 上使用了 sticky_header 包之后。我现在已经成功获取标题了。只是没有设法在正确的标题下获取消息。
数据集:
[
{
"time": "2020-06-16T10:31:12.000Z",
"message": "P2 BGM-01 HV buiten materieel (Gas lekkage) Franckstraat Arnhem 073631"
},
{
"time": "2020-06-16T10:29:35.000Z",
"message": "A1 Brahmslaan 3862TD Nijkerk 73278"
},
{
"time": "2020-06-16T10:29:35.000Z",
"message": "A2 NS Station Rheden Dr. Langemijerweg 6991EV Rheden 73286"
},
{
"time": "2020-06-15T09:41:18.000Z",
"message": "A2 VWS Utrechtseweg 6871DR Renkum 74636"
},
{
"time": "2020-06-14T09:40:58.000Z",
"message": "B2 5623EJ : Michelangelolaan Eindhoven Obj: ziekenhuizen 8610 Ca CATH route 522 PAAZ Rit: 66570"
}
]
首先,分解代码以从数据集中获取按日期映射的分组。然后使用这张图来构建 UI。
使用 collection 包来使用 groupBy 函数。
import "package:collection/collection.dart";
void main() {
final dataSet = [
{
"time": "2020-06-16T10:31:12.000Z",
"message": "Message1",
},
{
"time": "2020-06-16T10:29:35.000Z",
"message": "Message2",
},
{
"time": "2020-06-15T09:41:18.000Z",
"message": "Message3",
},
];
var groupByDate = groupBy(dataSet, (obj) => obj['time'].substring(0, 10));
groupByDate.forEach((date, list) {
// Header
print('${date}:');
// Group
list.forEach((listItem) {
// List item
print('${listItem["time"]}, ${listItem["message"]}');
});
// day section divider
print('\n');
});
}
输出:
2020-06-16:
2020-06-16T10:31:12.000Z, Message1
2020-06-16T10:29:35.000Z, Message2
2020-06-15:
2020-06-15T09:41:18.000Z, Message3
我希望这段代码能够帮助您开始构建您的 UI。 您可以使用 ListView 小部件来显示每个组中的列表项。
通过 DateTime、DateFormat 类来处理日期、时间值。
您应该能够通过一些自定义的约会逻辑来实现这一点。如果不在这些列表项之间添加文本标题,请检查两个连续日期是否相同。
以下是上述方法的实现。
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
void main() {
final dateString = '2020-06-16T10:31:12.000Z';
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Map> list = [
{
"time": "2020-06-16T10:31:12.000Z",
"message":
"P2 BGM-01 HV buiten materieel (Gas lekkage) Franckstraat Arnhem 073631"
},
{
"time": "2020-06-16T10:29:35.000Z",
"message": "A1 Brahmslaan 3862TD Nijkerk 73278"
},
{
"time": "2020-06-16T10:29:35.000Z",
"message": "A2 NS Station Rheden Dr. Langemijerweg 6991EV Rheden 73286"
},
{
"time": "2020-06-15T09:41:18.000Z",
"message": "A2 VWS Utrechtseweg 6871DR Renkum 74636"
},
{
"time": "2020-06-14T09:40:58.000Z",
"message":
"B2 5623EJ : Michelangelolaan Eindhoven Obj: ziekenhuizen 8610 Ca CATH route 522 PAAZ Rit: 66570"
}
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: ListView.builder(
itemCount: list.length,
itemBuilder: (_, index) {
bool isSameDate = true;
final String dateString = list[index]['time'];
final DateTime date = DateTime.parse(dateString);
final item = list[index];
if (index == 0) {
isSameDate = false;
} else {
final String prevDateString = list[index - 1]['time'];
final DateTime prevDate = DateTime.parse(prevDateString);
isSameDate = date.isSameDate(prevDate);
}
if (index == 0 || !(isSameDate)) {
return Column(children: [
Text(date.formatDate()),
ListTile(title: Text('item $index'))
]);
} else {
return ListTile(title: Text('item $index'));
}
}),
),
);
}
}
const String dateFormatter = 'MMMM dd, y';
extension DateHelper on DateTime {
String formatDate() {
final formatter = DateFormat(dateFormatter);
return formatter.format(this);
}
bool isSameDate(DateTime other) {
return this.year == other.year &&
this.month == other.month &&
this.day == other.day;
}
int getDifferenceInDaysWithNow() {
final now = DateTime.now();
return now.difference(this).inDays;
}
}
有一些名为 sticky_grouped_list 和 的 dart.pub 软件包(在您的问题中提到)。他们通过公共属性围绕组元素做了很好的工作。我已经尝试过 sticky_grouped_list 而且我没有在您的特定场景中使用它,一旦列表可以按动态类型对元素进行分组,它似乎适用于它。
除了外观之外,您的代码与该示例之间的主要功能差异是它们使用了 DateTime 元素而不是 String 作为对列表进行分组的更好方式。粘性组列表视图很有趣,因为当向下滚动时,当前日期会粘在屏幕顶部。
instead of using a ListView create a GroupedListView Widget:
GroupedListView<dynamic, String>(
elements: _elements,
groupBy: (element) => element['group'],
groupSeparatorBuilder: (String groupByValue) => Text(groupByValue),
itemBuilder: (context, dynamic element) => Text(element['name']),
order: GroupedListOrder.ASC,
),
使用 StickyGroupedListView,您还可以按自定义对象进行分组:
StickyGroupedListView<CustomObject, DateTime>(
elements: pedidos,
order: StickyGroupedListOrder.ASC,
groupBy: (CustomObject element) => DateTime(
element.dateGroup.year,
element.dateGroup.month,
element.dateGroup.day),
groupSeparatorBuilder: (CustomObject element) =>
renderDataCabecalho(element),
itemBuilder: (context, element) {
return _buildPedidoItemView(element, context);
},
),
如果它不适用于您的需求,您可能可以了解他们对元素进行分组的方法。
如果API支持排序,按照正确的顺序获取结果将是最简单、性能最高的解决方案。
I think you should filter data in the model to create groups follow the date.
class ListDate {
int id;
String name;
String date;
List<DateItem> results;
}
class DateItem {
String id;
String date;
String name;
}
After parse JSON to model, you can run listView by ListDate().results
您可以按日期对对象进行分组(例如,考虑到您的示例,是一天的开始),然后显示您想要的任何内容(没有库)。
示例:
1 - 我们有一些课程,其日期分组依据:
class WalletAccountRecord {
String id;
String name;
DateTime date;
....
2 - 我们有包含此类项目的列表(在我们的例子中来自 api,但任何构建方式都可以用于方法理解)
List<WalletAccountRecord> records = response.records;
3 - 然后我们按日期(按一天的开始)对此列表进行分组
Map<Jiffy, List<WalletAccountRecord>> recordsByDays = {};
for (var record in records) {
Jiffy date = Jiffy.parseFromDateTime(record.date).startOf(Unit.day);
recordsByDays.update(date, (List<WalletAccountRecord> value) => [record, ...value], ifAbsent: () => [record]);
}
_logger.d('Records by Dates: ${recordsByDays.length} days -> ${recordsByDays.keys}');
4 - 现在,考虑到所有数据都分组到 Map 中,我们可以按照想要的方式显示它。我们的 UI 的完整示例:
class WalletRecords extends StatelessWidget {
final AnalyticsService _analyticsService = Get.find();
final WalletRecordsStateController _walletRecordsStateController = Get.find();
final Logger _logger = LoggerHelper.logger();
@override
Widget build(BuildContext context) {
return Obx(() => _recordsItems());
}
Widget _recordsItems() {
RecordsSearchResultRx recordsRx = _walletRecordsStateController.recordsGet();
//TODO Revision flow. if no wallet (when??) then just show 'no records yet'
//TODO no wallet/loading handle
List<WalletAccountRecord> records = recordsRx.records;
Map<Jiffy, List<WalletAccountRecord>> recordsByDays = {};
for (var record in records) {
Jiffy date = Jiffy.parseFromDateTime(record.date).startOf(Unit.day);
recordsByDays.update(date, (List<WalletAccountRecord> value) => [record, ...value], ifAbsent: () => [record]);
}
_logger.d('Records by Dates: ${recordsByDays.length} days -> ${recordsByDays.keys}');
return _recordsByDates(recordsByDays);
}
Widget _recordsByDates(Map<Jiffy, List<WalletAccountRecord>> recordsByDays) {
return ListView.builder(
itemCount: recordsByDays.length,
shrinkWrap: true,
itemBuilder: (context, int index) {
var date = recordsByDays.keys.elementAt(index);
return _recordsByDate(date, recordsByDays[date] ?? []);
},
);
}
Widget _recordsByDate(Jiffy date, List<WalletAccountRecord> recordsByDay) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 3),
child: Container(
decoration: BoxDecoration(
color: UiConstants.colorPrimaryDark.withOpacity(0.3),
border: Border.all(color: Colors.grey),
borderRadius: const BorderRadius.all(Radius.circular(15))),
child: Column(children: [
Padding(
padding: const EdgeInsets.only(left: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(date.format(pattern: 'd.MM.yy'),
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600))),
Wrap(children: [
IconButton(icon: const Icon(LineIcons.plus), onPressed: () => {}),
IconButton(icon: const Icon(LineIcons.pen), onPressed: () => {}),
]),
],
),
),
const Divider(height: 5, indent: 10, endIndent: 10),
_recordItems(recordsByDay),
]),
),
);
}
Widget _recordItems(List<WalletAccountRecord> records) {
return ListView.separated(
itemCount: records.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (BuildContext context, int index) => const Divider(height: 1, indent: 10, endIndent: 10),
itemBuilder: (context, index) {
return _recordOverview(records[index]);
},
);
}
Widget _recordOverview(WalletAccountRecord record) {
return ListTile(
dense: true,
horizontalTitleGap: 0,
title: Text(record.category!, style: const TextStyle(fontWeight: FontWeight.bold)),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(record.account.accountName),
Text(record.operationType.name),
Text(record.note ?? ''),
],
),
contentPadding: const EdgeInsets.only(left: 3),
trailing: Text('${record.amount.amount} ${record.amount.currencyCode}'),
//todo dynamic based on account color per account
leading: Container(color: Colors.blue, width: 5, padding: EdgeInsets.zero, margin: EdgeInsets.zero),
);
}
}