@@ -9,6 +9,10 @@ import 'package:uuid/uuid.dart';
99part 'create_headline_event.dart' ;
1010part 'create_headline_state.dart' ;
1111
12+ final class _FetchNextCountryPage extends CreateHeadlineEvent {
13+ const _FetchNextCountryPage ();
14+ }
15+
1216const _searchDebounceDuration = Duration (milliseconds: 300 );
1317
1418/// A BLoC to manage the state of creating a new headline.
@@ -20,11 +24,11 @@ class CreateHeadlineBloc
2024 required DataRepository <Source > sourcesRepository,
2125 required DataRepository <Topic > topicsRepository,
2226 required DataRepository <Country > countriesRepository,
23- }) : _headlinesRepository = headlinesRepository,
24- _sourcesRepository = sourcesRepository,
25- _topicsRepository = topicsRepository,
26- _countriesRepository = countriesRepository,
27- super (const CreateHeadlineState ()) {
27+ }) : _headlinesRepository = headlinesRepository,
28+ _sourcesRepository = sourcesRepository,
29+ _topicsRepository = topicsRepository,
30+ _countriesRepository = countriesRepository,
31+ super (const CreateHeadlineState ()) {
2832 on < CreateHeadlineDataLoaded > (_onDataLoaded);
2933 on < CreateHeadlineTitleChanged > (_onTitleChanged);
3034 on < CreateHeadlineExcerptChanged > (_onExcerptChanged);
@@ -35,6 +39,7 @@ class CreateHeadlineBloc
3539 on < CreateHeadlineCountryChanged > (_onCountryChanged);
3640 on < CreateHeadlineStatusChanged > (_onStatusChanged);
3741 on < CreateHeadlineSubmitted > (_onSubmitted);
42+ on < _FetchNextCountryPage > (_onFetchNextCountryPage);
3843 }
3944
4045 final DataRepository <Headline > _headlinesRepository;
@@ -78,26 +83,8 @@ class CreateHeadlineBloc
7883
7984 // After the initial page of countries is loaded, start a background
8085 // process to fetch all remaining pages.
81- //
82- // This approach is used for the following reasons:
83- // 1. UI Consistency: It allows us to use the standard
84- // `DropdownButtonFormField`, which is used elsewhere in the app.
85- // 2. Technical Limitation: The standard dropdown does not expose a
86- //
87- // The UI will update progressively and silently in the background as
88- // more data arrives.
89- while (state.countriesHasMore) {
90- final nextCountries = await _countriesRepository.readAll (
91- pagination: PaginationOptions (cursor: state.countriesCursor),
92- sort: [const SortOption ('name' , SortOrder .asc)],
93- );
94- emit (
95- state.copyWith (
96- countries: List .of (state.countries)..addAll (nextCountries.items),
97- countriesCursor: nextCountries.cursor,
98- countriesHasMore: nextCountries.hasMore,
99- ),
100- );
86+ if (state.countriesHasMore) {
87+ add (const _FetchNextCountryPage ());
10188 }
10289 } on HttpException catch (e) {
10390 emit (state.copyWith (status: CreateHeadlineStatus .failure, exception: e));
@@ -172,6 +159,49 @@ class CreateHeadlineBloc
172159 );
173160 }
174161
162+ // --- Background Data Fetching for Dropdown ---
163+ // The DropdownButtonFormField widget does not natively support on-scroll
164+ // pagination. To preserve UI consistency across the application, this BLoC
165+ // employs an event-driven background fetching mechanism.
166+ //
167+ // After the first page of items is loaded, a chain of events is initiated
168+ // to progressively fetch all remaining pages. This process is throttled
169+ // and runs in the background, ensuring the UI remains responsive while the
170+ // full list of dropdown options is populated over time.
171+ Future <void > _onFetchNextCountryPage (
172+ _FetchNextCountryPage event,
173+ Emitter <CreateHeadlineState > emit,
174+ ) async {
175+ if (! state.countriesHasMore || state.countriesIsLoadingMore) return ;
176+
177+ try {
178+ emit (state.copyWith (countriesIsLoadingMore: true ));
179+
180+ await Future .delayed (const Duration (milliseconds: 400 ));
181+
182+ final nextCountries = await _countriesRepository.readAll (
183+ pagination: PaginationOptions (cursor: state.countriesCursor),
184+ sort: [const SortOption ('name' , SortOrder .asc)],
185+ );
186+
187+ emit (
188+ state.copyWith (
189+ countries: List .of (state.countries)..addAll (nextCountries.items),
190+ countriesCursor: nextCountries.cursor,
191+ countriesHasMore: nextCountries.hasMore,
192+ countriesIsLoadingMore: false ,
193+ ),
194+ );
195+
196+ if (nextCountries.hasMore) {
197+ add (const _FetchNextCountryPage ());
198+ }
199+ } catch (e) {
200+ emit (state.copyWith (countriesIsLoadingMore: false ));
201+ // Optionally log the error without disrupting the user
202+ }
203+ }
204+
175205 Future <void > _onSubmitted (
176206 CreateHeadlineSubmitted event,
177207 Emitter <CreateHeadlineState > emit,
0 commit comments