@@ -9,6 +9,10 @@ import 'package:uuid/uuid.dart';
9
9
part 'create_headline_event.dart' ;
10
10
part 'create_headline_state.dart' ;
11
11
12
+ final class _FetchNextCountryPage extends CreateHeadlineEvent {
13
+ const _FetchNextCountryPage ();
14
+ }
15
+
12
16
const _searchDebounceDuration = Duration (milliseconds: 300 );
13
17
14
18
/// A BLoC to manage the state of creating a new headline.
@@ -20,11 +24,11 @@ class CreateHeadlineBloc
20
24
required DataRepository <Source > sourcesRepository,
21
25
required DataRepository <Topic > topicsRepository,
22
26
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 ()) {
28
32
on < CreateHeadlineDataLoaded > (_onDataLoaded);
29
33
on < CreateHeadlineTitleChanged > (_onTitleChanged);
30
34
on < CreateHeadlineExcerptChanged > (_onExcerptChanged);
@@ -35,6 +39,7 @@ class CreateHeadlineBloc
35
39
on < CreateHeadlineCountryChanged > (_onCountryChanged);
36
40
on < CreateHeadlineStatusChanged > (_onStatusChanged);
37
41
on < CreateHeadlineSubmitted > (_onSubmitted);
42
+ on < _FetchNextCountryPage > (_onFetchNextCountryPage);
38
43
}
39
44
40
45
final DataRepository <Headline > _headlinesRepository;
@@ -78,26 +83,8 @@ class CreateHeadlineBloc
78
83
79
84
// After the initial page of countries is loaded, start a background
80
85
// 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 ());
101
88
}
102
89
} on HttpException catch (e) {
103
90
emit (state.copyWith (status: CreateHeadlineStatus .failure, exception: e));
@@ -172,6 +159,49 @@ class CreateHeadlineBloc
172
159
);
173
160
}
174
161
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
+
175
205
Future <void > _onSubmitted (
176
206
CreateHeadlineSubmitted event,
177
207
Emitter <CreateHeadlineState > emit,
0 commit comments