Skip to content

Commit fa60c98

Browse files
committed
refactor(headlines-feed): migrate TopicFilterPage to use HeadlinesFilterBloc
- Replace TopicsFilterBloc with HeadlinesFilterBloc for topic management - Convert TopicFilterPage from StatefulWidget to StatelessWidget - Remove local topic selection management, use centralized HeadlinesFilterBloc - Update UI to reflect changes in state from HeadlinesFilterBloc - Simplify follow filter logic, integrate with HeadlinesFilterBloc - Remove unnecessary scroll controller and pagination logic
1 parent 9e0ac84 commit fa60c98

File tree

1 file changed

+46
-81
lines changed

1 file changed

+46
-81
lines changed

lib/headlines-feed/view/topic_filter_page.dart

Lines changed: 46 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,21 @@
11
import 'package:core/core.dart';
22
import 'package:flutter/material.dart';
3-
import 'package:flutter/material.dart';
43
import 'package:flutter_bloc/flutter_bloc.dart';
54
import 'package:flutter_news_app_mobile_client_full_source_code/app/bloc/app_bloc.dart';
6-
import 'package:flutter_news_app_mobile_client_full_source_code/headlines-feed/bloc/topics_filter_bloc.dart';
5+
import 'package:flutter_news_app_mobile_client_full_source_code/headlines-feed/bloc/headlines_filter_bloc.dart';
76
import 'package:flutter_news_app_mobile_client_full_source_code/l10n/l10n.dart';
8-
import 'package:go_router/go_router.dart';
97
import 'package:ui_kit/ui_kit.dart';
108

119
/// {@template topic_filter_page}
1210
/// A page dedicated to selecting news topics for filtering headlines.
11+
///
12+
/// This page now interacts with the centralized [HeadlinesFilterBloc]
13+
/// to manage the list of available topics and the user's selections.
1314
/// {@endtemplate}
14-
class TopicFilterPage extends StatefulWidget {
15+
class TopicFilterPage extends StatelessWidget {
1516
/// {@macro topic_filter_page}
1617
const TopicFilterPage({super.key});
1718

18-
@override
19-
State<TopicFilterPage> createState() => _TopicFilterPageState();
20-
}
21-
22-
class _TopicFilterPageState extends State<TopicFilterPage> {
23-
final _scrollController = ScrollController();
24-
late final TopicsFilterBloc _topicsFilterBloc;
25-
late Set<Topic> _pageSelectedTopics;
26-
27-
@override
28-
void initState() {
29-
super.initState();
30-
_scrollController.addListener(_onScroll);
31-
_topicsFilterBloc = context.read<TopicsFilterBloc>()
32-
..add(TopicsFilterRequested());
33-
34-
WidgetsBinding.instance.addPostFrameCallback((_) {
35-
// Initialize local selection from GoRouter extra parameter
36-
final initialSelection = GoRouterState.of(context).extra as List<Topic>?;
37-
_pageSelectedTopics = Set.from(initialSelection ?? []);
38-
});
39-
}
40-
41-
@override
42-
void dispose() {
43-
_scrollController.dispose();
44-
super.dispose();
45-
}
46-
47-
void _onScroll() {
48-
if (_isBottom) {
49-
_topicsFilterBloc.add(TopicsFilterLoadMoreRequested());
50-
}
51-
}
52-
53-
bool get _isBottom {
54-
if (!_scrollController.hasClients) return false;
55-
final maxScroll = _scrollController.position.maxScrollExtent;
56-
final currentScroll = _scrollController.offset;
57-
return currentScroll >= (maxScroll * 0.9);
58-
}
59-
6019
@override
6120
Widget build(BuildContext context) {
6221
final l10n = AppLocalizationsX(context).l10n;
@@ -70,13 +29,16 @@ class _TopicFilterPageState extends State<TopicFilterPage> {
7029
),
7130
actions: [
7231
// Apply My Followed Topics Button
73-
BlocBuilder<AppBloc, AppState>(
74-
builder: (context, appState) {
32+
BlocBuilder<HeadlinesFilterBloc, HeadlinesFilterState>(
33+
builder: (context, filterState) {
34+
final appState = context.watch<AppBloc>().state;
7535
final followedTopics =
7636
appState.userContentPreferences?.followedTopics ?? [];
37+
38+
// Determine if the current selection matches the followed topics
7739
final isFollowedFilterActive = followedTopics.isNotEmpty &&
78-
_pageSelectedTopics.length == followedTopics.length &&
79-
_pageSelectedTopics.containsAll(followedTopics);
40+
filterState.selectedTopics.length == followedTopics.length &&
41+
filterState.selectedTopics.containsAll(followedTopics);
8042

8143
return IconButton(
8244
icon: isFollowedFilterActive
@@ -87,9 +49,6 @@ class _TopicFilterPageState extends State<TopicFilterPage> {
8749
: null,
8850
tooltip: l10n.headlinesFeedFilterApplyFollowedLabel,
8951
onPressed: () {
90-
setState(() {
91-
_pageSelectedTopics = Set.from(followedTopics);
92-
});
9352
if (followedTopics.isEmpty) {
9453
ScaffoldMessenger.of(context)
9554
..hideCurrentSnackBar()
@@ -99,27 +58,35 @@ class _TopicFilterPageState extends State<TopicFilterPage> {
9958
duration: const Duration(seconds: 3),
10059
),
10160
);
61+
} else {
62+
// Toggle the followed items filter in the HeadlinesFilterBloc
63+
context.read<HeadlinesFilterBloc>().add(
64+
FollowedItemsFilterToggled(
65+
isUsingFollowedItems: !isFollowedFilterActive,
66+
),
67+
);
10268
}
10369
},
10470
);
10571
},
10672
),
107-
// Apply Filters Button
73+
// Apply Filters Button (now just pops, as state is managed centrally)
10874
IconButton(
10975
icon: const Icon(Icons.check),
11076
tooltip: l10n.headlinesFeedFilterApplyButton,
11177
onPressed: () {
112-
context.pop(_pageSelectedTopics.toList());
78+
// The selections are already managed by HeadlinesFilterBloc.
79+
// Just pop the page.
80+
Navigator.of(context).pop();
11381
},
11482
),
11583
],
11684
),
117-
body: BlocBuilder<TopicsFilterBloc, TopicsFilterState>(
118-
builder: (context, state) {
85+
body: BlocBuilder<HeadlinesFilterBloc, HeadlinesFilterState>(
86+
builder: (context, filterState) {
11987
// Determine overall loading status for the main list
12088
final isLoadingMainList =
121-
state.status == TopicsFilterStatus.initial ||
122-
state.status == TopicsFilterStatus.loading;
89+
filterState.status == HeadlinesFilterStatus.loading;
12390

12491
if (isLoadingMainList) {
12592
return LoadingStateWidget(
@@ -129,21 +96,27 @@ class _TopicFilterPageState extends State<TopicFilterPage> {
12996
);
13097
}
13198

132-
if (state.status == TopicsFilterStatus.failure &&
133-
state.topics.isEmpty) {
99+
if (filterState.status == HeadlinesFilterStatus.failure &&
100+
filterState.allTopics.isEmpty) {
134101
return Center(
135102
child: FailureStateWidget(
136-
exception:
137-
state.error ??
103+
exception: filterState.error ??
138104
const UnknownException(
139105
'An unknown error occurred while fetching topics.',
140106
),
141-
onRetry: () => _topicsFilterBloc.add(TopicsFilterRequested()),
107+
onRetry: () => context.read<HeadlinesFilterBloc>().add(
108+
FilterDataLoaded(
109+
initialSelectedTopics: filterState.selectedTopics.toList(),
110+
initialSelectedSources: filterState.selectedSources.toList(),
111+
initialSelectedCountries: filterState.selectedCountries.toList(),
112+
isUsingFollowedItems: filterState.isUsingFollowedItems,
113+
),
114+
),
142115
),
143116
);
144117
}
145118

146-
if (state.topics.isEmpty) {
119+
if (filterState.allTopics.isEmpty) {
147120
return InitialStateWidget(
148121
icon: Icons.category_outlined,
149122
headline: l10n.topicFilterEmptyHeadline,
@@ -152,27 +125,19 @@ class _TopicFilterPageState extends State<TopicFilterPage> {
152125
}
153126

154127
return ListView.builder(
155-
controller: _scrollController,
156-
itemCount: state.hasMore
157-
? state.topics.length + 1
158-
: state.topics.length,
128+
itemCount: filterState.allTopics.length,
159129
itemBuilder: (context, index) {
160-
if (index >= state.topics.length) {
161-
return const Center(child: CircularProgressIndicator());
162-
}
163-
final topic = state.topics[index];
164-
final isSelected = _pageSelectedTopics.contains(topic);
130+
final topic = filterState.allTopics[index];
131+
final isSelected = filterState.selectedTopics.contains(topic);
165132
return CheckboxListTile(
166133
title: Text(topic.name),
167134
value: isSelected,
168135
onChanged: (bool? value) {
169-
setState(() {
170-
if (value == true) {
171-
_pageSelectedTopics.add(topic);
172-
} else {
173-
_pageSelectedTopics.remove(topic);
174-
}
175-
});
136+
if (value != null) {
137+
context.read<HeadlinesFilterBloc>().add(
138+
FilterTopicToggled(topic: topic, isSelected: value),
139+
);
140+
}
176141
},
177142
);
178143
},

0 commit comments

Comments
 (0)