Skip to content

Commit 51a22da

Browse files
feat: Add filter for categories (#108)
1 parent 1c9910e commit 51a22da

File tree

13 files changed

+1053
-83
lines changed

13 files changed

+1053
-83
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## 1.3.0
6+
7+
### Added
8+
- Added support to filter notifications by category.
9+
510
## 1.2.1
611

712
### Added

README.md

Lines changed: 191 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -102,74 +102,225 @@ Given below are the arguments of Siren Inbox Widget.
102102
| ----------------- | -------------------------------------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
103103
| darkMode | Toggle to enable dark mode when custom theme is not passed | bool | false |
104104
| hideTab | Toggle to enable all and unread tabs | bool | false |
105-
| itemsPerFetch | Number of notifications fetch per api request (have a max cap of 50) | int | 20 |
105+
| itemsPerFetch | Number of notifications fetch per api request (max 50) | int | 20 |
106106
| listEmptyWidget | Custom widget for empty notification list | Widget | null |
107-
| customCard | Custom widget to display the notification cards | Widget | null |
107+
| customCard | Custom builder for notification cards | Widget Function(NotificationType) | null |
108108
| customLoader | Custom widget to display the initial loading state | Widget | null |
109109
| customErrorWidget | Custom error widget | Widget | null |
110110
| cardParams | Properties of notification card | CardParams | CardParams(hideAvatar: false, disableAutoMarkAsRead: false, hideDelete: false, deleteIcon: Icon(Icons.close), onAvatarClick: Function(NotificationType), hideMediaThumbnail: false, onMediaThumbnailClick: Function(NotificationType)) |
111-
| headerParams | Properties of notification window header | HeaderParams | HeaderParams(hideHeader: false, hideClearAll: false,title: 'Notifications', customHeader: null showBackButton:false, backButton: null, onBackPress: ()=> null ) |
112-
| tabParams | Properties of tab bar | TabParams | TabParams(tabs: [TabItem(key: 'ALL', title: 'All'), TabItem(key: 'UNREAD', title: 'Unread')], activeTabIndex:0,) |
111+
| headerParams | Properties of notification window header | HeaderParams | HeaderParams(hideHeader: false, hideClearAll: false, title: 'Notifications', customHeader: null, showBackButton: false, backButton: null, onBackPress: null) |
112+
| tabParams | Properties of tab bar | TabParams | TabParams(tabs: [TabItem(key: 'ALL', title: 'All'), TabItem(key: 'UNREAD', title: 'Unread')], activeTabIndex: 0) |
113113
| onCardClick | Custom click handler for notification cards | Function(NotificationType) | null |
114114
| onError | Callback for handling errors | Function(SirenErrorType) | null |
115115
| theme | Theme properties for custom color theme | CustomThemeColors | null |
116116
| customStyles | Style properties for custom styling | CustomStyles | null |
117+
| customTabIndicator| Custom decoration for tab indicator | BoxDecoration | null |
118+
| filterParams | Properties for configuring the filter dropdown | FilterParams | FilterParams(categoryFilterParams: CategoryFilterParams(showFilters: true, filterIconWidget: null, hideBadge: false, categoryFilterStyles: CategoryFilterStyles(dropdownTextStyle: null))) |
117119

118120
#### Theme customization
119121

120-
Here are some of the available theme options:
122+
Here are the available theme options:
121123

122124
```dart
123125
theme: CustomThemeColors(
124-
primary: Colors.blue,
125-
highlightedCardColor: Colors.blueAccent,
126-
textColor: Colors.green,
127-
cardColors: CardColors(
128-
titleColor: Colors.grey,
129-
subtitleColor: Colors.grey,
130-
),
131-
inboxHeaderColors: InboxHeaderColors(
132-
titleColor: Colors.redAccent,
133-
headerActionColor: Colors.purpleAccent,
134-
borderColor: Colors.cyanAccent
135-
),
136-
),
126+
backgroundColor: Colors.blue,
127+
primary: Colors.blueAccent,
128+
highlightedCardColor: Colors.blue.shade100,
129+
borderColor: Colors.grey.shade300,
130+
deleteIcon: Colors.red,
131+
clearAllIcon: Colors.grey,
132+
textColor: Colors.black87,
133+
dateColor: Colors.grey,
134+
timerIcon: Colors.blue,
135+
notificationIconColor: Colors.blue,
136+
loaderColor: Colors.blue,
137+
inboxHeaderColors: InboxHeaderColors(
138+
background: Colors.white,
139+
titleColor: Colors.black87,
140+
headerActionColor: Colors.blue,
141+
borderColor: Colors.grey.shade300
142+
),
143+
badgeColors: BadgeColors(
144+
backgroundColor: Colors.red,
145+
color: Colors.white
146+
),
147+
cardColors: CardColors(
148+
borderColor: Colors.grey.shade300,
149+
background: Colors.white,
150+
titleColor: Colors.black87,
151+
subtitleColor: Colors.grey,
152+
descriptionColor: Colors.black54
153+
),
154+
tabColors: TabColors(
155+
containerBackgroundColor: Colors.white,
156+
activeTabBackgroundColor: Colors.blue.shade50,
157+
activeTabTextColor: Colors.blue,
158+
inactiveTabTextColor: Colors.grey,
159+
indicatorColor: Colors.blue
160+
),
161+
filterColors: FilterColors(
162+
categoryFilterColors: CategoryFilterColors(
163+
filterIconBorderColor: Colors.grey.shade300,
164+
filterBadgeColor: Colors.blue,
165+
filterDropdownBackgroundColor: Colors.white,
166+
filterCheckboxCheckedColor: Colors.blue,
167+
filterCheckboxUncheckedColor: Colors.grey.shade300,
168+
filterActionTextColor: Colors.black87,
169+
filterIconColor: Colors.blue,
170+
checkIconColor: Colors.white
171+
)
172+
)
173+
)
137174
```
138175

139-
#### Style options
176+
#### Style customization
140177

141-
Here are some of the custom style options for the notification inbox:
178+
Here are the custom style options for the notification inbox:
142179

143180
```dart
144181
customStyles: CustomStyles(
145182
container: ContainerStyle(
146-
padding: EdgeInsets.all(20),
147-
decoration: BoxDecoration(color: Colors.yellow)),
183+
padding: EdgeInsets.all(16),
184+
decoration: BoxDecoration(
185+
color: Colors.white,
186+
borderRadius: BorderRadius.circular(8)
187+
),
188+
margin: EdgeInsets.all(8)
189+
),
148190
cardStyle: CardStyle(
149191
cardContainer: ContainerStyle(
150-
padding: EdgeInsets.all(20),
192+
padding: EdgeInsets.all(16),
151193
decoration: BoxDecoration(
152-
color: Colors.yellow,
153-
border: Border.all(color: Colors.red))),
154-
cardTitle: TextStyle(fontSize: 22, fontWeight: FontWeight.w800),
155-
cardSubtitle:
156-
TextStyle(fontSize: 20, fontWeight: FontWeight.w700),
157-
cardDescription:
158-
TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
159-
dateStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
160-
avatarSize: 30,
194+
color: Colors.white,
195+
border: Border.all(color: Colors.grey.shade300),
196+
borderRadius: BorderRadius.circular(8)
197+
)
198+
),
199+
cardTitle: TextStyle(
200+
fontSize: 16,
201+
fontWeight: FontWeight.w600,
202+
color: Colors.black87
203+
),
204+
cardSubtitle: TextStyle(
205+
fontSize: 14,
206+
fontWeight: FontWeight.w500,
207+
color: Colors.grey
208+
),
209+
cardDescription: TextStyle(
210+
fontSize: 14,
211+
color: Colors.black54
161212
),
213+
dateStyle: TextStyle(
214+
fontSize: 12,
215+
color: Colors.grey
216+
),
217+
avatarSize: 40
218+
),
162219
appBarStyle: InboxHeaderStyle(
163-
headerTextStyle:
164-
TextStyle(fontSize: 20, fontWeight: FontWeight.w900),
165-
titlePadding: EdgeInsets.symmetric(horizontal: 30),
166-
borderWidth: 5),
167-
timerIconStyle: TimerIconStyle(size: 30),
168-
deleteIconStyle: DeleteIconStyle(size: 30),
169-
clearAllIconStyle: ClearAllIconStyle(size: 30),
170-
),
220+
headerTextStyle: TextStyle(
221+
fontSize: 18,
222+
fontWeight: FontWeight.w600,
223+
color: Colors.black87
224+
),
225+
titlePadding: EdgeInsets.symmetric(horizontal: 16),
226+
borderWidth: 1
227+
),
228+
notificationIconStyle: NotificationIconStyle(
229+
size: 24
230+
),
231+
badgeStyle: BadgeStyle(
232+
fontSize: 12,
233+
size: 20,
234+
top: 0,
235+
right: 2
236+
),
237+
timerIconStyle: TimerIconStyle(
238+
size: 20
239+
),
240+
deleteIconStyle: DeleteIconStyle(
241+
size: 20
242+
),
243+
clearAllIconStyle: ClearAllIconStyle(
244+
size: 20
245+
),
246+
tabStyles: TabStyles(
247+
containerStyle: ContainerStyle(
248+
padding: EdgeInsets.symmetric(horizontal: 16),
249+
margin: EdgeInsets.only(bottom: 8)
250+
),
251+
activeTabTextStyle: TextStyle(
252+
fontSize: 14,
253+
fontWeight: FontWeight.w600,
254+
color: Colors.blue
255+
),
256+
inActiveTabTextStyle: TextStyle(
257+
fontSize: 14,
258+
fontWeight: FontWeight.w500,
259+
color: Colors.grey
260+
),
261+
indicatorSize: 2,
262+
indicatorPadding: EdgeInsets.symmetric(horizontal: 16)
263+
),
264+
hideTabMargin: HideTabMargin(
265+
upper: false,
266+
lower: false
267+
),
268+
filterStyles: FilterStyles(
269+
categoryFilterStyles: CategoryFilterStyles(
270+
dropdownTextStyle: TextStyle(
271+
fontSize: 14,
272+
color: Colors.black87
273+
)
274+
)
275+
)
276+
)
277+
```
278+
279+
### 2.4. Filter Configuration
280+
281+
The filter functionality allows users to filter notifications by categories. Here's how to configure it:
282+
283+
```dart
284+
SirenInbox(
285+
filterParams: FilterParams(
286+
categoryFilterParams: CategoryFilterParams(
287+
showFilters: true,
288+
filterIconWidget: Icon(Icons.filter_list), // Optional custom filter icon
289+
hideBadge: false, // Optional hide badge showing number of selected filters
290+
categoryFilterStyles: CategoryFilterStyles( // Optional custom styles for category filter
291+
dropdownTextStyle: TextStyle(
292+
fontSize: 14,
293+
color: Colors.black87
294+
)
295+
)
296+
)
297+
)
298+
)
299+
```
300+
301+
#### Filter Features:
302+
- Custom filter icon support
303+
- Badge showing number of selected filters (99+ for more than 99 selections)
304+
- Dropdown with checkbox selection
305+
- Customizable colors and styles for all filter components
306+
307+
#### Category Filter Styles
308+
You can customize the appearance of the category filter dropdown using `CategoryFilterStyles`:
309+
310+
```dart
311+
CategoryFilterStyles(
312+
dropdownTextStyle: TextStyle(
313+
fontSize: 14,
314+
color: Colors.black87,
315+
fontWeight: FontWeight.w500
316+
)
317+
)
171318
```
172319

320+
| Style Property | Description | Type | Default Value |
321+
|------------------|------------------------------------------------|-----------|----------------------------------|
322+
| dropdownTextStyle| Style for the category text in dropdown | TextStyle | fontSize: 14, color: Colors.black87 |
323+
173324
## 3. Siren Class
174325

175326
The `Siren Class` provides utility functions for modifying notifications.

lib/src/api/fetch_all_notification.dart

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class FetchAllNotifications {
2929
bool? isRead,
3030
String? start,
3131
String? end,
32+
List<String>? categories,
3233
}) async {
3334
final apiPath =
3435
'${Generics.V2}${Generics.BASE_URL}${SirenDataProvider.instance.recipientId}/notifications';
@@ -53,8 +54,22 @@ class FetchAllNotifications {
5354
queryParams['isRead'] = isRead.toString();
5455
}
5556

56-
final queryString =
57-
queryParams.entries.map((e) => '${e.key}=${e.value}').join('&');
57+
// Build the query string
58+
final queryParts = <String>[];
59+
60+
// Add all non-category parameters
61+
queryParams.forEach((key, value) {
62+
queryParts.add('$key=${Uri.encodeComponent(value)}');
63+
});
64+
65+
// Add each category as a separate parameter
66+
if (categories != null && categories.isNotEmpty) {
67+
for (final category in categories) {
68+
queryParts.add('category=${Uri.encodeComponent(category)}');
69+
}
70+
}
71+
72+
final queryString = queryParts.join('&');
5873

5974
if (SirenDataProvider.instance.tokenVerificationStatus != Status.SUCCESS) {
6075
apiError = SirenDataProvider.instance.getVerificationErrorType();

lib/src/api/fetch_categories.dart

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import 'package:sirenapp_flutter_inbox/src/constants/generics.dart';
2+
import 'package:sirenapp_flutter_inbox/src/data/siren_data_provider.dart';
3+
import 'package:sirenapp_flutter_inbox/src/errors/errors.dart';
4+
import 'package:sirenapp_flutter_inbox/src/models/api_response.dart';
5+
import 'package:sirenapp_flutter_inbox/src/services/api_client.dart';
6+
import 'package:sirenapp_flutter_inbox/src/services/api_provider.dart';
7+
8+
class FetchCategories {
9+
FetchCategories._internal();
10+
static final FetchCategories instance = FetchCategories._internal();
11+
final ApiClient api = ApiClient(apiProvider());
12+
13+
List<String> convertJsonToCategoryList(List<dynamic> dataList) {
14+
return dataList.map((dynamic json) {
15+
if (json is String) {
16+
return json;
17+
}
18+
throw const FormatException('Invalid JSON format');
19+
}).toList();
20+
}
21+
22+
Future<ApiResponse> fetchCategories() async {
23+
final apiPath =
24+
'${Generics.V2}${Generics.BASE_URL}${SirenDataProvider.instance.recipientId}/categories';
25+
final result = ApiResponse()..isLoading = true;
26+
var apiError = Errors.notificationFetchFailedError;
27+
28+
if (SirenDataProvider.instance.tokenVerificationStatus != Status.SUCCESS) {
29+
apiError = SirenDataProvider.instance.getVerificationErrorType();
30+
result
31+
..isLoading = false
32+
..isError = true
33+
..data = null
34+
..rawResponse = Errors.rawResponseError
35+
..error = apiError;
36+
return result;
37+
}
38+
39+
final apiResponse = await api.get(
40+
path: apiPath,
41+
);
42+
43+
if (apiResponse.statusCode != 0 && apiResponse.data != null) {
44+
final dataList =
45+
ApiResponse.fromJson(apiResponse.data).data as List<dynamic>?;
46+
result
47+
..isLoading = false
48+
..isSuccess = apiResponse.statusCode == 200
49+
..isError = apiResponse.statusCode != 200
50+
..data = convertJsonToCategoryList(dataList ?? [])
51+
..rawResponse = apiResponse
52+
..error = apiError;
53+
} else {
54+
result
55+
..isLoading = false
56+
..isSuccess = false
57+
..isError = true
58+
..rawResponse = apiResponse
59+
..error = Errors.defaultError;
60+
}
61+
62+
return result;
63+
}
64+
}

0 commit comments

Comments
 (0)