class SearchPostsFilter with SearchProviderService {
final formatTimestamp = FormatDate();
void filterPostsToBest() {
final sortedVents = searchPostsProvider.vents
.toList()
..sort((a, b) => a.totalLikes.compareTo(b.totalLikes));
searchPostsProvider.setVents(sortedVents);
}
void filterPostsToLatest() {
final sortedVents = searchPostsProvider.vents
.toList()
..sort((a, b) => formatTimestamp.parseFormattedTimestamp(b.postTimestamp)
.compareTo(formatTimestamp.parseFormattedTimestamp(a.postTimestamp)));
searchPostsProvider.setVents(sortedVents);
}
void filterPostsToOldest() {
final sortedVents = searchPostsProvider.vents
.toList()
..sort((a, b) => formatTimestamp.parseFormattedTimestamp(a.postTimestamp)
.compareTo(formatTimestamp.parseFormattedTimestamp(b.postTimestamp)));
searchPostsProvider.setVents(sortedVents);
}
void filterToControversial() {
final sortedVents = searchPostsProvider.vents
.where((post) => post.totalComments >= post.totalLikes)
.toList();
searchPostsProvider.setVents(sortedVents);
}
}
存储帖子信息的提供商/数据类:
class SearchVents {
String title;
String creator;
String postTimestamp;
Uint8List profilePic;
int totalLikes;
int totalComments;
bool isPostLiked;
bool isPostSaved;
SearchVents({
required this.title,
required this.creator,
required this.postTimestamp,
required this.profilePic,
this.totalLikes = 0,
this.totalComments = 0,
this.isPostLiked = false,
this.isPostSaved = false
});
}
class SearchPostsProvider extends ChangeNotifier {
List<SearchVents> _vents = [];
List<SearchVents> get vents => _vents;
void setVents(List<SearchVents> vents) {
_vents = vents;
notifyListeners();
}
}
帖子listView:
class SearchPostsListView extends StatefulWidget {
const SearchPostsListView({super.key});
@override
State<SearchPostsListView> createState() => _SearchPostsListViewState();
}
class _SearchPostsListViewState extends State<SearchPostsListView> {
final sortOptionsNotifier = ValueNotifier<String>('Best');
final searchPostsFilter = SearchPostsFilter();
Widget _buildVentPreview(SearchVents ventData) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.5),
child: DefaultVentPreviewer(
title: ventData.title,
bodyText: '',
creator: ventData.creator,
postTimestamp: ventData.postTimestamp,
totalLikes: ventData.totalLikes,
totalComments: ventData.totalComments,
pfpData: ventData.profilePic,
useV2ActionButtons: true,
),
);
}
void _sortOptionsOnPressed({required String filter}) {
switch (filter) {
case == 'Best':
searchPostsFilter.filterPostsToBest();
break;
case == 'Latest':
searchPostsFilter.filterPostsToLatest();
break;
case == 'Oldest':
searchPostsFilter.filterPostsToOldest();
break;
case == 'Controversial':
searchPostsFilter.filterToControversial();
break;
}
sortOptionsNotifier.value = filter;
Navigator.pop(context);
}
Widget _buildFilterButtons({
required ValueListenable notifier,
required VoidCallback onPressed
}) {
return SizedBox(
height: 35,
child: InkWellEffect(
onPressed: onPressed,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(width: 8),
const Icon(CupertinoIcons.chevron_down, color: ThemeColor.thirdWhite, size: 15),
const SizedBox(width: 8),
ValueListenableBuilder(
valueListenable: notifier,
builder: (_, filterText, __) {
return Text(
filterText,
style: GoogleFonts.inter(
color: ThemeColor.thirdWhite,
fontWeight: FontWeight.w800,
fontSize: 13
)
);
},
),
const SizedBox(width: 8),
],
),
),
);
}
Widget _buildListView(List<SearchVents> ventDataList) {
return DynamicHeightGridView(
physics: const AlwaysScrollableScrollPhysics(
parent: BouncingScrollPhysics(),
),
crossAxisCount: 1,
itemCount: ventDataList.length + 1,
builder: (_, index) {
if (index == 0) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 12),
Row(
children: [
_buildFilterButtons(
notifier: sortOptionsNotifier,
onPressed: () {
BottomsheetSearchFilter().buildSortOptionsBottomsheet(
context: context,
currentFilter: sortOptionsNotifier.value,
bestOnPressed: () => _sortOptionsOnPressed(filter: 'Best'),
latestOnPressed: () => _sortOptionsOnPressed(filter: 'Latest'),
oldestOnPressed: () => _sortOptionsOnPressed(filter: 'Oldest'),
controversialOnPressed: () => _sortOptionsOnPressed(filter: 'Controversial'),
);
}
),
],
),
const SizedBox(height: 4),
],
);
}
final reversedVentIndex = ventDataList.length - index;
if (reversedVentIndex >= 0) {
final vents = ventDataList[reversedVentIndex];
return _buildVentPreview(vents);
}
return const SizedBox.shrink();
},
);
}
@override
void dispose() {
sortOptionsNotifier.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
searchPostsFilter.filterPostsToBest();
}
@override
Widget build(BuildContext context) {
return Consumer<SearchPostsProvider>(
builder: (_, ventData, __) {
final ventDataList = ventData.vents;
return _buildListView(ventDataList);
}
);
}
}
现在,即使列表视图未正确排序,也要牢记,当我打印数据列表时,它可以准确地显示了如何对其进行排序,但不会反映在listView。
void filterPostsToOldest() {
final sortedVents = searchPostsProvider.vents
.toList()
..sort((a, b) => formatTimestamp.parseFormattedTimestamp(a.postTimestamp)
.compareTo(formatTimestamp.parseFormattedTimestamp(b.postTimestamp)));
for(var post in sortedVents) {
print('${post.title} / ${post.postTimestamp}');
}
searchPostsProvider.setVents(sortedVents);
}
因此,我尝试在listView上使用keyedSubtree,如Chatgpt所建议,但它不起作用,没有任何更改
return KeyedSubtree(
key: ValueKey('${vents.title}/${vents.creator}'),
child: _buildVentPreview(vents),
);
Tldr; ListView与列表数据不正确匹配。
您不需要键控子树,您只需在返回的列中添加一个
UniqueKey
即可。一个键将识别列表视图中的每个元素,因此当您重新排列它们时,它们正确识别了,因此您必须在子树中的最顶部添加键,在这种情况下,对于每个视图卡成为
builder
,所以只要给它一个
Column