Skip to content

Commit 19e0223

Browse files
committed
refactor(content_management): implement efficient background fetching for countries dropdown
- Introduce _FetchNextCountryPage event for paginated data fetching - Replace while loop with event-driven approach for better performance - Add loading state for countries to improve user experience - Optimize code structure and formatting for better readability
1 parent c3fbeb6 commit 19e0223

File tree

1 file changed

+56
-26
lines changed

1 file changed

+56
-26
lines changed

lib/content_management/bloc/edit_headline/edit_headline_bloc.dart

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import 'package:flutter/foundation.dart';
88
part 'edit_headline_event.dart';
99
part 'edit_headline_state.dart';
1010

11+
final class _FetchNextCountryPage extends EditHeadlineEvent {
12+
const _FetchNextCountryPage();
13+
}
14+
1115
const _searchDebounceDuration = Duration(milliseconds: 300);
1216

1317
/// A BLoC to manage the state of editing a single headline.
@@ -19,12 +23,12 @@ class EditHeadlineBloc extends Bloc<EditHeadlineEvent, EditHeadlineState> {
1923
required DataRepository<Topic> topicsRepository,
2024
required DataRepository<Country> countriesRepository,
2125
required String headlineId,
22-
}) : _headlinesRepository = headlinesRepository,
23-
_sourcesRepository = sourcesRepository,
24-
_topicsRepository = topicsRepository,
25-
_countriesRepository = countriesRepository,
26-
_headlineId = headlineId,
27-
super(const EditHeadlineState()) {
26+
}) : _headlinesRepository = headlinesRepository,
27+
_sourcesRepository = sourcesRepository,
28+
_topicsRepository = topicsRepository,
29+
_countriesRepository = countriesRepository,
30+
_headlineId = headlineId,
31+
super(const EditHeadlineState()) {
2832
on<EditHeadlineLoaded>(_onLoaded);
2933
on<EditHeadlineTitleChanged>(_onTitleChanged);
3034
on<EditHeadlineExcerptChanged>(_onExcerptChanged);
@@ -35,6 +39,7 @@ class EditHeadlineBloc extends Bloc<EditHeadlineEvent, EditHeadlineState> {
3539
on<EditHeadlineCountryChanged>(_onCountryChanged);
3640
on<EditHeadlineStatusChanged>(_onStatusChanged);
3741
on<EditHeadlineSubmitted>(_onSubmitted);
42+
on<_FetchNextCountryPage>(_onFetchNextCountryPage);
3843
}
3944

4045
final DataRepository<Headline> _headlinesRepository;
@@ -89,26 +94,8 @@ class EditHeadlineBloc extends Bloc<EditHeadlineEvent, EditHeadlineState> {
8994

9095
// After the initial page of countries is loaded, start a background
9196
// process to fetch all remaining pages.
92-
//
93-
// This approach is used for the following reasons:
94-
// 1. UI Consistency: It allows us to use the standard
95-
// `DropdownButtonFormField`, which is used elsewhere in the app.
96-
// 2. Technical Limitation: The standard dropdown does not expose a
97-
//
98-
// The UI will update progressively and silently in the background as
99-
// more data arrives.
100-
while (state.countriesHasMore) {
101-
final nextCountries = await _countriesRepository.readAll(
102-
pagination: PaginationOptions(cursor: state.countriesCursor),
103-
sort: [const SortOption('name', SortOrder.asc)],
104-
);
105-
emit(
106-
state.copyWith(
107-
countries: List.of(state.countries)..addAll(nextCountries.items),
108-
countriesCursor: nextCountries.cursor,
109-
countriesHasMore: nextCountries.hasMore,
110-
),
111-
);
97+
if (state.countriesHasMore) {
98+
add(const _FetchNextCountryPage());
11299
}
113100
} on HttpException catch (e) {
114101
emit(state.copyWith(status: EditHeadlineStatus.failure, exception: e));
@@ -210,6 +197,49 @@ class EditHeadlineBloc extends Bloc<EditHeadlineEvent, EditHeadlineState> {
210197
);
211198
}
212199

200+
// --- Background Data Fetching for Dropdown ---
201+
// The DropdownButtonFormField widget does not natively support on-scroll
202+
// pagination. To preserve UI consistency across the application, this BLoC
203+
// employs an event-driven background fetching mechanism.
204+
//
205+
// After the first page of items is loaded, a chain of events is initiated
206+
// to progressively fetch all remaining pages. This process is throttled
207+
// and runs in the background, ensuring the UI remains responsive while the
208+
// full list of dropdown options is populated over time.
209+
Future<void> _onFetchNextCountryPage(
210+
_FetchNextCountryPage event,
211+
Emitter<EditHeadlineState> emit,
212+
) async {
213+
if (!state.countriesHasMore || state.countriesIsLoadingMore) return;
214+
215+
try {
216+
emit(state.copyWith(countriesIsLoadingMore: true));
217+
218+
await Future.delayed(const Duration(milliseconds: 400));
219+
220+
final nextCountries = await _countriesRepository.readAll(
221+
pagination: PaginationOptions(cursor: state.countriesCursor),
222+
sort: [const SortOption('name', SortOrder.asc)],
223+
);
224+
225+
emit(
226+
state.copyWith(
227+
countries: List.of(state.countries)..addAll(nextCountries.items),
228+
countriesCursor: nextCountries.cursor,
229+
countriesHasMore: nextCountries.hasMore,
230+
countriesIsLoadingMore: false,
231+
),
232+
);
233+
234+
if (nextCountries.hasMore) {
235+
add(const _FetchNextCountryPage());
236+
}
237+
} catch (e) {
238+
emit(state.copyWith(countriesIsLoadingMore: false));
239+
// Optionally log the error without disrupting the user
240+
}
241+
}
242+
213243
Future<void> _onSubmitted(
214244
EditHeadlineSubmitted event,
215245
Emitter<EditHeadlineState> emit,

0 commit comments

Comments
 (0)