I'm trying to implement this posts-filter functions using Provider in Flutter, the posts has title, timestamp, pfp, and number of likes/comments, my problem is let's say if the user sorted to Oldest what happen is that the value of likes/comments sorted correctly but the title/pfp/timestamp are not and remain static in their current position but until I scrolled all the way down and up the ListView updated and the bug fixed by itself, why?
The filter class:
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); }}
The provider/data classes where the post info is stored:
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(); }}
The posts 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); } ); }}
Now keep in mind even when ListView is not correctly sorted, somehow when I printed the data list it shows exactly how it should be sorted but it does not reflect on the 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); }
So I tried to use KeyedSubtree on the ListView as suggested by ChatGPT but it does not work and nothing changes
return KeyedSubtree( key: ValueKey('${vents.title}/${vents.creator}'), child: _buildVentPreview(vents), );
TLDR; ListView doesn't correctly match with list data.