Skip to content

Commit bacd19f

Browse files
committed
refactor: The BLoCs for the headlines feed, search, and account features are now provided at the GoRouter level. The BlocProvider has been removed from the individual pages, and the HeadlinesFilterPage is now correctly accessing the HeadlinesFeedBloc from the context.
1 parent 73013f5 commit bacd19f

File tree

6 files changed

+173
-141
lines changed

6 files changed

+173
-141
lines changed

lib/account/view/account_page.dart

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
33
import 'package:go_router/go_router.dart';
44
import 'package:ht_authentication_client/ht_authentication_client.dart';
5-
import 'package:ht_authentication_repository/ht_authentication_repository.dart';
65
import 'package:ht_main/account/bloc/account_bloc.dart';
76
import 'package:ht_main/app/bloc/app_bloc.dart';
87
import 'package:ht_main/l10n/l10n.dart';
@@ -19,17 +18,7 @@ class AccountPage extends StatelessWidget {
1918

2019
@override
2120
Widget build(BuildContext context) {
22-
return BlocProvider(
23-
create:
24-
(context) => AccountBloc(
25-
authenticationRepository:
26-
context.read<HtAuthenticationRepository>(),
27-
),
28-
// Use BlocListener if specific feedback (like snackbars for errors)
29-
// is needed from AccountBloc actions, though redirects are handled
30-
// globally.
31-
child: const _AccountView(),
32-
);
21+
return const _AccountView();
3322
}
3423
}
3524

lib/app/view/app.dart

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// ignore_for_file: deprecated_member_use
33

44
import 'dart:async';
5+
56
import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
67
import 'package:flutter/material.dart';
78
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -20,22 +21,22 @@ import 'package:ht_sources_repository/ht_sources_repository.dart';
2021

2122
class App extends StatelessWidget {
2223
const App({
23-
required HtHeadlinesRepository htHeadlinesRepository,
2424
required HtAuthenticationRepository htAuthenticationRepository,
25+
required HtHeadlinesRepository htHeadlinesRepository,
2526
required HtCategoriesRepository htCategoriesRepository,
2627
required HtCountriesRepository htCountriesRepository,
2728
required HtSourcesRepository htSourcesRepository,
2829
required HtKVStorageService kvStorageService,
2930
super.key,
30-
}) : _htHeadlinesRepository = htHeadlinesRepository,
31-
_htAuthenticationRepository = htAuthenticationRepository,
31+
}) : _htAuthenticationRepository = htAuthenticationRepository,
32+
_htHeadlinesRepository = htHeadlinesRepository,
3233
_htCategoriesRepository = htCategoriesRepository,
3334
_htCountriesRepository = htCountriesRepository,
3435
_htSourcesRepository = htSourcesRepository,
3536
_kvStorageService = kvStorageService;
3637

37-
final HtHeadlinesRepository _htHeadlinesRepository;
3838
final HtAuthenticationRepository _htAuthenticationRepository;
39+
final HtHeadlinesRepository _htHeadlinesRepository;
3940
final HtCategoriesRepository _htCategoriesRepository;
4041
final HtCountriesRepository _htCountriesRepository;
4142
final HtSourcesRepository _htSourcesRepository;
@@ -45,8 +46,8 @@ class App extends StatelessWidget {
4546
Widget build(BuildContext context) {
4647
return MultiRepositoryProvider(
4748
providers: [
48-
RepositoryProvider.value(value: _htHeadlinesRepository),
4949
RepositoryProvider.value(value: _htAuthenticationRepository),
50+
RepositoryProvider.value(value: _htHeadlinesRepository),
5051
RepositoryProvider.value(value: _htCategoriesRepository),
5152
RepositoryProvider.value(value: _htCountriesRepository),
5253
RepositoryProvider.value(value: _htSourcesRepository),
@@ -70,14 +71,32 @@ class App extends StatelessWidget {
7071
),
7172
),
7273
],
73-
child: const _AppView(),
74+
child: _AppView(
75+
htAuthenticationRepository: _htAuthenticationRepository,
76+
htHeadlinesRepository: _htHeadlinesRepository,
77+
htCategoriesRepository: _htCategoriesRepository,
78+
htCountriesRepository: _htCountriesRepository,
79+
htSourcesRepository: _htSourcesRepository,
80+
),
7481
),
7582
);
7683
}
7784
}
7885

7986
class _AppView extends StatefulWidget {
80-
const _AppView();
87+
const _AppView({
88+
required this.htAuthenticationRepository,
89+
required this.htHeadlinesRepository,
90+
required this.htCategoriesRepository,
91+
required this.htCountriesRepository,
92+
required this.htSourcesRepository,
93+
});
94+
95+
final HtAuthenticationRepository htAuthenticationRepository;
96+
final HtHeadlinesRepository htHeadlinesRepository;
97+
final HtCategoriesRepository htCategoriesRepository;
98+
final HtCountriesRepository htCountriesRepository;
99+
final HtSourcesRepository htSourcesRepository;
81100

82101
@override
83102
State<_AppView> createState() => _AppViewState();
@@ -95,7 +114,14 @@ class _AppViewState extends State<_AppView> {
95114
final appBloc = context.read<AppBloc>();
96115
// Initialize the notifier with the BLoC's current state
97116
_statusNotifier = ValueNotifier<AppStatus>(appBloc.state.status);
98-
_router = createRouter(authStatusNotifier: _statusNotifier);
117+
_router = createRouter(
118+
authStatusNotifier: _statusNotifier,
119+
htAuthenticationRepository: widget.htAuthenticationRepository,
120+
htHeadlinesRepository: widget.htHeadlinesRepository,
121+
htCategoriesRepository: widget.htCategoriesRepository,
122+
htCountriesRepository: widget.htCountriesRepository,
123+
htSourcesRepository: widget.htSourcesRepository,
124+
);
99125

100126
// --- Initialize Deep Link Handling ---
101127
_initDynamicLinks();

lib/headlines-feed/view/headlines_feed_page.dart

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
66
import 'package:go_router/go_router.dart';
77
// Import Category
88
// Import Country
9-
import 'package:ht_headlines_repository/ht_headlines_repository.dart';
109
import 'package:ht_main/headlines-feed/bloc/headlines_feed_bloc.dart';
1110
import 'package:ht_main/headlines-feed/widgets/headline_item_widget.dart';
1211
import 'package:ht_main/l10n/l10n.dart';
@@ -28,13 +27,7 @@ class HeadlinesFeedPage extends StatelessWidget {
2827

2928
@override
3029
Widget build(BuildContext context) {
31-
return BlocProvider(
32-
create:
33-
(context) => HeadlinesFeedBloc(
34-
headlinesRepository: context.read<HtHeadlinesRepository>(),
35-
)..add(const HeadlinesFeedFetchRequested()),
36-
child: const _HeadlinesFeedView(),
37-
);
30+
return const _HeadlinesFeedView();
3831
}
3932
}
4033

@@ -142,7 +135,8 @@ class _HeadlinesFeedViewState extends State<_HeadlinesFeedView> {
142135
tooltip: l10n.headlinesFeedFilterTooltip,
143136
onPressed: () {
144137
// Navigate to the filter page route
145-
final headlinesFeedBloc = context.read<HeadlinesFeedBloc>();
138+
final headlinesFeedBloc =
139+
context.read<HeadlinesFeedBloc>();
146140
context.goNamed(
147141
Routes.feedFilterName,
148142
extra: headlinesFeedBloc,

lib/headlines-feed/view/headlines_filter_page.dart

Lines changed: 91 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ import 'package:ht_sources_client/ht_sources_client.dart';
2222
/// {@endtemplate}
2323
class HeadlinesFilterPage extends StatefulWidget {
2424
/// {@macro headlines_filter_page}
25-
const HeadlinesFilterPage({required this.headlinesFeedBloc, super.key});
26-
27-
final HeadlinesFeedBloc headlinesFeedBloc;
25+
const HeadlinesFilterPage({super.key});
2826

2927
@override
3028
State<HeadlinesFilterPage> createState() => _HeadlinesFilterPageState();
@@ -40,7 +38,7 @@ class _HeadlinesFilterPageState extends State<HeadlinesFilterPage> {
4038
void initState() {
4139
super.initState();
4240
// Initialize temporary state from the currently active filters in the BLoC
43-
final currentState = widget.headlinesFeedBloc.state;
41+
final currentState = BlocProvider.of<HeadlinesFeedBloc>(context).state;
4442
if (currentState is HeadlinesFeedLoaded) {
4543
_tempSelectedCategories = List.from(currentState.filter.categories ?? []);
4644
_tempSelectedSources = List.from(currentState.filter.sources ?? []);
@@ -98,103 +96,101 @@ class _HeadlinesFilterPageState extends State<HeadlinesFilterPage> {
9896
);
9997
}
10098

99+
@override
101100
@override
102101
Widget build(BuildContext context) {
103102
final l10n = context.l10n;
104103

105-
return BlocProvider.value(
106-
value: widget.headlinesFeedBloc,
107-
child: Scaffold(
108-
appBar: AppBar(
109-
leading: IconButton(
110-
icon: const Icon(Icons.close),
111-
tooltip: MaterialLocalizations.of(context).closeButtonTooltip,
112-
onPressed: () => context.pop(), // Discard changes
104+
return Scaffold(
105+
appBar: AppBar(
106+
leading: IconButton(
107+
icon: const Icon(Icons.close),
108+
tooltip: MaterialLocalizations.of(context).closeButtonTooltip,
109+
onPressed: () => context.pop(), // Discard changes
110+
),
111+
title: Text(l10n.headlinesFeedFilterTitle),
112+
actions: [
113+
// Clear Button
114+
IconButton(
115+
icon: const Icon(Icons.clear_all),
116+
tooltip: l10n.headlinesFeedFilterResetButton,
117+
onPressed: () {
118+
// Dispatch clear event immediately and pop
119+
context.read<HeadlinesFeedBloc>().add(
120+
HeadlinesFeedFiltersCleared(),
121+
);
122+
context.pop();
123+
},
113124
),
114-
title: Text(l10n.headlinesFeedFilterTitle),
115-
actions: [
116-
// Clear Button
117-
IconButton(
118-
icon: const Icon(Icons.clear_all),
119-
tooltip: l10n.headlinesFeedFilterResetButton,
120-
onPressed: () {
121-
// Dispatch clear event immediately and pop
122-
context.read<HeadlinesFeedBloc>().add(
123-
HeadlinesFeedFiltersCleared(),
124-
);
125-
context.pop();
126-
},
127-
),
128-
// Apply Button
129-
IconButton(
130-
icon: const Icon(Icons.check),
131-
tooltip: l10n.headlinesFeedFilterApplyButton,
132-
onPressed: () {
133-
// Apply the temporary filters to the BLoC
134-
context.read<HeadlinesFeedBloc>().add(
135-
HeadlinesFeedFiltersApplied(
136-
filter: HeadlineFilter(
137-
categories:
138-
_tempSelectedCategories.isNotEmpty
139-
? _tempSelectedCategories
140-
: null,
141-
sources:
142-
_tempSelectedSources.isNotEmpty
143-
? _tempSelectedSources
144-
: null,
145-
eventCountries:
146-
_tempSelectedCountries.isNotEmpty
147-
? _tempSelectedCountries
148-
: null,
149-
),
125+
// Apply Button
126+
IconButton(
127+
icon: const Icon(Icons.check),
128+
tooltip: l10n.headlinesFeedFilterApplyButton,
129+
onPressed: () {
130+
// Apply the temporary filters to the BLoC
131+
context.read<HeadlinesFeedBloc>().add(
132+
HeadlinesFeedFiltersApplied(
133+
filter: HeadlineFilter(
134+
categories:
135+
_tempSelectedCategories.isNotEmpty
136+
? _tempSelectedCategories
137+
: null,
138+
sources:
139+
_tempSelectedSources.isNotEmpty
140+
? _tempSelectedSources
141+
: null,
142+
eventCountries:
143+
_tempSelectedCountries.isNotEmpty
144+
? _tempSelectedCountries
145+
: null,
150146
),
151-
);
152-
context.pop(); // Close the filter page
153-
},
154-
),
155-
],
156-
),
157-
body: ListView(
158-
padding: const EdgeInsets.symmetric(vertical: AppSpacing.md),
159-
children: [
160-
_buildFilterTile(
161-
context: context,
162-
title: l10n.headlinesFeedFilterCategoryLabel,
163-
selectedCount: _tempSelectedCategories.length,
164-
routeName: Routes.feedFilterCategoriesName,
165-
currentSelection: _tempSelectedCategories,
166-
onResult: (result) {
167-
if (result is List<Category>) {
168-
setState(() => _tempSelectedCategories = result);
169-
}
170-
},
171-
),
172-
_buildFilterTile(
173-
context: context,
174-
title: l10n.headlinesFeedFilterSourceLabel,
175-
selectedCount: _tempSelectedSources.length,
176-
routeName: Routes.feedFilterSourcesName,
177-
currentSelection: _tempSelectedSources,
178-
onResult: (result) {
179-
if (result is List<Source>) {
180-
setState(() => _tempSelectedSources = result);
181-
}
182-
},
183-
),
184-
_buildFilterTile(
185-
context: context,
186-
title: l10n.headlinesFeedFilterEventCountryLabel,
187-
selectedCount: _tempSelectedCountries.length,
188-
routeName: Routes.feedFilterCountriesName,
189-
currentSelection: _tempSelectedCountries,
190-
onResult: (result) {
191-
if (result is List<Country>) {
192-
setState(() => _tempSelectedCountries = result);
193-
}
194-
},
195-
),
196-
],
197-
),
147+
),
148+
);
149+
context.pop(); // Close the filter page
150+
},
151+
),
152+
],
153+
),
154+
body: ListView(
155+
padding: const EdgeInsets.symmetric(vertical: AppSpacing.md),
156+
children: [
157+
_buildFilterTile(
158+
context: context,
159+
title: l10n.headlinesFeedFilterCategoryLabel,
160+
selectedCount: _tempSelectedCategories.length,
161+
routeName: Routes.feedFilterCategoriesName,
162+
currentSelection: _tempSelectedCategories,
163+
onResult: (result) {
164+
if (result is List<Category>) {
165+
setState(() => _tempSelectedCategories = result);
166+
}
167+
},
168+
),
169+
_buildFilterTile(
170+
context: context,
171+
title: l10n.headlinesFeedFilterSourceLabel,
172+
selectedCount: _tempSelectedSources.length,
173+
routeName: Routes.feedFilterSourcesName,
174+
currentSelection: _tempSelectedSources,
175+
onResult: (result) {
176+
if (result is List<Source>) {
177+
setState(() => _tempSelectedSources = result);
178+
}
179+
},
180+
),
181+
_buildFilterTile(
182+
context: context,
183+
title: l10n.headlinesFeedFilterEventCountryLabel,
184+
selectedCount: _tempSelectedCountries.length,
185+
routeName: Routes.feedFilterCountriesName,
186+
currentSelection: _tempSelectedCountries,
187+
onResult: (result) {
188+
if (result is List<Country>) {
189+
setState(() => _tempSelectedCountries = result);
190+
}
191+
},
192+
),
193+
],
198194
),
199195
);
200196
}

lib/headlines-search/view/headlines_search_page.dart

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
import 'package:flutter/material.dart';
55
import 'package:flutter_bloc/flutter_bloc.dart';
6-
import 'package:ht_headlines_repository/ht_headlines_repository.dart';
76
import 'package:ht_main/headlines-feed/widgets/headline_item_widget.dart';
87
import 'package:ht_main/headlines-search/bloc/headlines_search_bloc.dart';
98
import 'package:ht_main/l10n/l10n.dart';
@@ -22,14 +21,7 @@ class HeadlinesSearchPage extends StatelessWidget {
2221

2322
@override
2423
Widget build(BuildContext context) {
25-
return BlocProvider(
26-
create:
27-
(_) => HeadlinesSearchBloc(
28-
headlinesRepository: context.read<HtHeadlinesRepository>(),
29-
),
30-
// The actual UI is built by the private _HeadlinesSearchView widget.
31-
child: const _HeadlinesSearchView(),
32-
);
24+
return const _HeadlinesSearchView();
3325
}
3426
}
3527

0 commit comments

Comments
 (0)