11import 'package:core/core.dart' ;
22import 'package:flutter/material.dart' ;
3+ import 'package:flutter/material.dart' ;
34import 'package:flutter_bloc/flutter_bloc.dart' ;
5+ import 'package:flutter_news_app_mobile_client_full_source_code/app/bloc/app_bloc.dart' ;
46import 'package:flutter_news_app_mobile_client_full_source_code/headlines-feed/bloc/topics_filter_bloc.dart' ;
57import 'package:flutter_news_app_mobile_client_full_source_code/l10n/l10n.dart' ;
68import 'package:go_router/go_router.dart' ;
@@ -68,16 +70,13 @@ class _TopicFilterPageState extends State<TopicFilterPage> {
6870 ),
6971 actions: [
7072 // Apply My Followed Topics Button
71- BlocBuilder <TopicsFilterBloc , TopicsFilterState >(
72- builder: (context, state) {
73- // Determine if the "Apply My Followed" icon should be filled
74- // This logic checks if all currently selected topics are
75- // also present in the fetched followed topics list.
76- final followedTopicsSet = state.followedTopics.toSet ();
77- final isFollowedFilterActive =
78- followedTopicsSet.isNotEmpty &&
79- _pageSelectedTopics.length == followedTopicsSet.length &&
80- _pageSelectedTopics.containsAll (followedTopicsSet);
73+ BlocBuilder <AppBloc , AppState >(
74+ builder: (context, appState) {
75+ final followedTopics =
76+ appState.userContentPreferences? .followedTopics ?? [];
77+ final isFollowedFilterActive = followedTopics.isNotEmpty &&
78+ _pageSelectedTopics.length == followedTopics.length &&
79+ _pageSelectedTopics.containsAll (followedTopics);
8180
8281 return IconButton (
8382 icon: isFollowedFilterActive
@@ -87,15 +86,21 @@ class _TopicFilterPageState extends State<TopicFilterPage> {
8786 ? theme.colorScheme.primary
8887 : null ,
8988 tooltip: l10n.headlinesFeedFilterApplyFollowedLabel,
90- onPressed:
91- state.followedTopicsStatus == TopicsFilterStatus .loading
92- ? null // Disable while loading
93- : () {
94- // Dispatch event to BLoC to fetch and apply followed topics
95- _topicsFilterBloc.add (
96- TopicsFilterApplyFollowedRequested (),
97- );
98- },
89+ onPressed: () {
90+ setState (() {
91+ _pageSelectedTopics = Set .from (followedTopics);
92+ });
93+ if (followedTopics.isEmpty) {
94+ ScaffoldMessenger .of (context)
95+ ..hideCurrentSnackBar ()
96+ ..showSnackBar (
97+ SnackBar (
98+ content: Text (l10n.noFollowedItemsForFilterSnackbar),
99+ duration: const Duration (seconds: 3 ),
100+ ),
101+ );
102+ }
103+ },
99104 );
100105 },
101106 ),
@@ -109,134 +114,70 @@ class _TopicFilterPageState extends State<TopicFilterPage> {
109114 ),
110115 ],
111116 ),
112- body: BlocListener <TopicsFilterBloc , TopicsFilterState >(
113- // Listen for changes in followedTopicsStatus or followedTopics
114- listenWhen: (previous, current) =>
115- previous.followedTopicsStatus != current.followedTopicsStatus ||
116- previous.followedTopics != current.followedTopics,
117- listener: (context, state) {
118- if (state.followedTopicsStatus == TopicsFilterStatus .success) {
119- // Update local state with followed topics from BLoC
120- setState (() {
121- _pageSelectedTopics = Set .from (state.followedTopics);
122- });
123- if (state.followedTopics.isEmpty) {
124- ScaffoldMessenger .of (context)
125- ..hideCurrentSnackBar ()
126- ..showSnackBar (
127- SnackBar (
128- content: Text (l10n.noFollowedItemsForFilterSnackbar),
129- duration: const Duration (seconds: 3 ),
130- ),
131- );
132- }
133- } else if (state.followedTopicsStatus == TopicsFilterStatus .failure) {
134- // Show error message if fetching followed topics failed
135- ScaffoldMessenger .of (context)
136- ..hideCurrentSnackBar ()
137- ..showSnackBar (
138- SnackBar (
139- content: Text (state.error? .message ?? l10n.unknownError),
140- duration: const Duration (seconds: 3 ),
141- ),
142- );
117+ body: BlocBuilder <TopicsFilterBloc , TopicsFilterState >(
118+ builder: (context, state) {
119+ // Determine overall loading status for the main list
120+ final isLoadingMainList =
121+ state.status == TopicsFilterStatus .initial ||
122+ state.status == TopicsFilterStatus .loading;
123+
124+ if (isLoadingMainList) {
125+ return LoadingStateWidget (
126+ icon: Icons .category_outlined,
127+ headline: l10n.topicFilterLoadingHeadline,
128+ subheadline: l10n.pleaseWait,
129+ );
143130 }
144- },
145- child: BlocBuilder <TopicsFilterBloc , TopicsFilterState >(
146- builder: (context, state) {
147- // Determine overall loading status for the main list
148- final isLoadingMainList =
149- state.status == TopicsFilterStatus .initial ||
150- state.status == TopicsFilterStatus .loading;
151-
152- // Determine if followed topics are currently loading
153- final isLoadingFollowedTopics =
154- state.followedTopicsStatus == TopicsFilterStatus .loading;
155131
156- if (isLoadingMainList) {
157- return LoadingStateWidget (
158- icon: Icons .category_outlined,
159- headline: l10n.topicFilterLoadingHeadline,
160- subheadline: l10n.pleaseWait,
161- );
162- }
163-
164- if (state.status == TopicsFilterStatus .failure &&
165- state.topics.isEmpty) {
166- return Center (
167- child: FailureStateWidget (
168- exception:
169- state.error ??
170- const UnknownException (
171- 'An unknown error occurred while fetching topics.' ,
172- ),
173- onRetry: () => _topicsFilterBloc.add (TopicsFilterRequested ()),
174- ),
175- );
176- }
132+ if (state.status == TopicsFilterStatus .failure &&
133+ state.topics.isEmpty) {
134+ return Center (
135+ child: FailureStateWidget (
136+ exception:
137+ state.error ??
138+ const UnknownException (
139+ 'An unknown error occurred while fetching topics.' ,
140+ ),
141+ onRetry: () => _topicsFilterBloc.add (TopicsFilterRequested ()),
142+ ),
143+ );
144+ }
177145
178- if (state.topics.isEmpty) {
179- return InitialStateWidget (
180- icon: Icons .category_outlined,
181- headline: l10n.topicFilterEmptyHeadline,
182- subheadline: l10n.topicFilterEmptySubheadline,
183- );
184- }
146+ if (state.topics.isEmpty) {
147+ return InitialStateWidget (
148+ icon: Icons .category_outlined,
149+ headline: l10n.topicFilterEmptyHeadline,
150+ subheadline: l10n.topicFilterEmptySubheadline,
151+ );
152+ }
185153
186- return Stack (
187- children: [
188- ListView .builder (
189- controller: _scrollController,
190- itemCount: state.hasMore
191- ? state.topics.length + 1
192- : state.topics.length,
193- itemBuilder: (context, index) {
194- if (index >= state.topics.length) {
195- return const Center (child: CircularProgressIndicator ());
154+ return ListView .builder (
155+ controller: _scrollController,
156+ itemCount: state.hasMore
157+ ? state.topics.length + 1
158+ : state.topics.length,
159+ 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);
165+ return CheckboxListTile (
166+ title: Text (topic.name),
167+ value: isSelected,
168+ onChanged: (bool ? value) {
169+ setState (() {
170+ if (value == true ) {
171+ _pageSelectedTopics.add (topic);
172+ } else {
173+ _pageSelectedTopics.remove (topic);
196174 }
197- final topic = state.topics[index];
198- final isSelected = _pageSelectedTopics.contains (topic);
199- return CheckboxListTile (
200- title: Text (topic.name),
201- value: isSelected,
202- onChanged: (bool ? value) {
203- setState (() {
204- if (value == true ) {
205- _pageSelectedTopics.add (topic);
206- } else {
207- _pageSelectedTopics.remove (topic);
208- }
209- });
210- },
211- );
212- },
213- ),
214- // Show loading overlay if followed topics are being fetched
215- if (isLoadingFollowedTopics)
216- Positioned .fill (
217- child: ColoredBox (
218- color: Colors .black54, // Semi-transparent overlay
219- child: Center (
220- child: Column (
221- mainAxisAlignment: MainAxisAlignment .center,
222- children: [
223- const CircularProgressIndicator (),
224- const SizedBox (height: AppSpacing .md),
225- Text (
226- l10n.headlinesFeedLoadingHeadline,
227- style: theme.textTheme.titleMedium? .copyWith (
228- color: Colors .white,
229- ),
230- ),
231- ],
232- ),
233- ),
234- ),
235- ),
236- ],
237- );
238- },
239- ),
175+ });
176+ },
177+ );
178+ },
179+ );
180+ },
240181 ),
241182 );
242183 }
0 commit comments