Skip to content

Commit b7b9b87

Browse files
committed
Refactor(filters): implement reorderable UI for saved filters
- Refactors the ManageSavedFiltersPage to use a ReorderableListView, enabling users to drag and drop their saved filters to change their order. - Replaces ListView.separated with ReorderableListView.builder. Implements the onReorder callback to update the list order and dispatch the SavedFiltersReordered event to the AppBloc. -Adds a ReorderableDragStartListener with a drag handle icon as the leading widget for each ListTile. Replaces the "Rename" and "Delete" IconButtons with a PopupMenuButton in the trailing widget for a cleaner UI.
1 parent a2a508a commit b7b9b87

File tree

1 file changed

+60
-24
lines changed

1 file changed

+60
-24
lines changed

lib/headlines-feed/view/manage_saved_filters_page.dart

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
3+
import 'package:core/core.dart';
34
import 'package:flutter_news_app_mobile_client_full_source_code/app/bloc/app_bloc.dart';
45
import 'package:flutter_news_app_mobile_client_full_source_code/headlines-feed/widgets/save_filter_dialog.dart';
56
import 'package:flutter_news_app_mobile_client_full_source_code/l10n/l10n.dart';
67
import 'package:ui_kit/ui_kit.dart';
78

89
/// {@template manage_saved_filters_page}
9-
/// A page for managing saved feed filters.
10+
/// A page for managing saved feed filters, allowing users to reorder,
11+
/// rename, or delete them.
1012
///
11-
/// Allows users to rename or delete their saved filters.
13+
/// Reordering is handled via a [ReorderableListView], which dispatches a
14+
/// [SavedFiltersReordered] event to the [AppBloc] to persist the new order.
15+
/// Renaming and deletion are handled via a [PopupMenuButton] on each list item.
1216
/// {@endtemplate}
1317
class ManageSavedFiltersPage extends StatelessWidget {
1418
/// {@macro manage_saved_filters_page}
@@ -22,6 +26,7 @@ class ManageSavedFiltersPage extends StatelessWidget {
2226
return Scaffold(
2327
appBar: AppBar(
2428
title: Text(
29+
// Will be updated to a new localization key later.
2530
l10n.manageFiltersPageTitle,
2631
style: theme.textTheme.titleLarge,
2732
),
@@ -32,27 +37,29 @@ class ManageSavedFiltersPage extends StatelessWidget {
3237

3338
if (savedFilters.isEmpty) {
3439
return InitialStateWidget(
40+
// Will be updated to new localization keys later.
3541
icon: Icons.filter_list_off_outlined,
3642
headline: l10n.manageFiltersEmptyHeadline,
3743
subheadline: l10n.manageFiltersEmptySubheadline,
3844
);
3945
}
4046

41-
return ListView.separated(
47+
return ReorderableListView.builder(
4248
itemCount: savedFilters.length,
43-
separatorBuilder: (context, index) =>
44-
const Divider(height: 1, indent: AppSpacing.md),
4549
itemBuilder: (context, index) {
4650
final filter = savedFilters[index];
4751
return ListTile(
52+
// A key is required for ReorderableListView to work correctly.
53+
key: ValueKey(filter.id),
54+
leading: ReorderableDragStartListener(
55+
index: index,
56+
child: const Icon(Icons.drag_handle),
57+
),
4858
title: Text(filter.name),
49-
trailing: Row(
50-
mainAxisSize: MainAxisSize.min,
51-
children: [
52-
IconButton(
53-
icon: const Icon(Icons.edit_outlined),
54-
tooltip: l10n.manageFiltersRenameTooltip,
55-
onPressed: () {
59+
trailing: PopupMenuButton<String>(
60+
onSelected: (value) {
61+
switch (value) {
62+
case 'rename':
5663
showDialog<void>(
5764
context: context,
5865
builder: (_) => SaveFilterDialog(
@@ -67,24 +74,53 @@ class ManageSavedFiltersPage extends StatelessWidget {
6774
},
6875
),
6976
);
70-
},
71-
),
72-
IconButton(
73-
icon: Icon(
74-
Icons.delete_outline,
75-
color: theme.colorScheme.error,
76-
),
77-
tooltip: l10n.manageFiltersDeleteTooltip,
78-
onPressed: () {
77+
break;
78+
case 'delete':
7979
context.read<AppBloc>().add(
8080
SavedFilterDeleted(filterId: filter.id),
8181
);
82-
},
83-
),
84-
],
82+
break;
83+
}
84+
},
85+
itemBuilder: (BuildContext context) =>
86+
<PopupMenuEntry<String>>[
87+
PopupMenuItem<String>(
88+
value: 'rename',
89+
// Will be updated to new localization keys later.
90+
child: Text(l10n.manageFiltersRenameTooltip),
91+
),
92+
PopupMenuItem<String>(
93+
value: 'delete',
94+
child: Text(
95+
// Will be updated to new localization keys later.
96+
l10n.manageFiltersDeleteTooltip,
97+
style: TextStyle(color: theme.colorScheme.error),
98+
),
99+
),
100+
],
85101
),
86102
);
87103
},
104+
onReorder: (oldIndex, newIndex) {
105+
// This adjustment is necessary when moving an item downwards
106+
// in the list.
107+
if (oldIndex < newIndex) {
108+
newIndex -= 1;
109+
}
110+
111+
// Create a mutable copy of the list.
112+
final reorderedList = List<SavedFilter>.from(savedFilters);
113+
114+
// Remove the item from its old position and insert it into the
115+
// new position.
116+
final movedFilter = reorderedList.removeAt(oldIndex);
117+
reorderedList.insert(newIndex, movedFilter);
118+
119+
// Dispatch the event to the AppBloc to persist the new order.
120+
context.read<AppBloc>().add(
121+
SavedFiltersReordered(reorderedFilters: reorderedList),
122+
);
123+
},
88124
);
89125
},
90126
),

0 commit comments

Comments
 (0)