1
+ //
2
+ // ignore_for_file: lines_longer_than_80_chars
3
+
1
4
import 'package:flutter/material.dart' ;
2
5
import 'package:flutter_bloc/flutter_bloc.dart' ;
3
6
import 'package:ht_headlines_repository/ht_headlines_repository.dart' ;
@@ -20,9 +23,10 @@ class HeadlinesSearchPage extends StatelessWidget {
20
23
@override
21
24
Widget build (BuildContext context) {
22
25
return BlocProvider (
23
- create: (_) => HeadlinesSearchBloc (
24
- headlinesRepository: context.read <HtHeadlinesRepository >(),
25
- ),
26
+ create:
27
+ (_) => HeadlinesSearchBloc (
28
+ headlinesRepository: context.read <HtHeadlinesRepository >(),
29
+ ),
26
30
// The actual UI is built by the private _HeadlinesSearchView widget.
27
31
child: const _HeadlinesSearchView (),
28
32
);
@@ -40,7 +44,8 @@ class _HeadlinesSearchView extends StatefulWidget {
40
44
41
45
class _HeadlinesSearchViewState extends State <_HeadlinesSearchView > {
42
46
final _scrollController = ScrollController ();
43
- final _textController = TextEditingController (); // Controller for the TextField
47
+ final _textController =
48
+ TextEditingController (); // Controller for the TextField
44
49
bool _showClearButton = false ; // State to control clear button visibility
45
50
46
51
@override
@@ -68,11 +73,11 @@ class _HeadlinesSearchViewState extends State<_HeadlinesSearchView> {
68
73
void _onScroll () {
69
74
final state = context.read <HeadlinesSearchBloc >().state;
70
75
if (_isBottom && state is HeadlinesSearchSuccess ) {
71
- final searchTerm = state.lastSearchTerm; // Use lastSearchTerm from state
76
+ final searchTerm = state.lastSearchTerm;
72
77
if (state.hasMore) {
73
78
context.read <HeadlinesSearchBloc >().add (
74
- HeadlinesSearchFetchRequested (searchTerm: searchTerm! ),
75
- );
79
+ HeadlinesSearchFetchRequested (searchTerm: searchTerm! ),
80
+ );
76
81
}
77
82
}
78
83
}
@@ -89,8 +94,8 @@ class _HeadlinesSearchViewState extends State<_HeadlinesSearchView> {
89
94
/// Triggers a search request based on the current text input.
90
95
void _performSearch () {
91
96
context.read <HeadlinesSearchBloc >().add (
92
- HeadlinesSearchFetchRequested (searchTerm: _textController.text),
93
- );
97
+ HeadlinesSearchFetchRequested (searchTerm: _textController.text),
98
+ );
94
99
}
95
100
96
101
@override
@@ -105,12 +110,13 @@ class _HeadlinesSearchViewState extends State<_HeadlinesSearchView> {
105
110
// Enhanced TextField integrated into the AppBar title
106
111
title: TextField (
107
112
controller: _textController,
108
- // Use text style that contrasts well with AppBar background
113
+
109
114
style: appBarTheme.titleTextStyle ?? theme.textTheme.titleLarge,
110
115
decoration: InputDecoration (
111
116
hintText: l10n.headlinesSearchHintText,
112
- // Use hint style that contrasts well
113
- hintStyle: appBarTheme.titleTextStyle? .copyWith (
117
+
118
+ hintStyle:
119
+ appBarTheme.titleTextStyle? .copyWith (
114
120
color: (appBarTheme.titleTextStyle? .color ??
115
121
colorScheme.onSurface)
116
122
.withOpacity (0.6 ),
@@ -126,28 +132,28 @@ class _HeadlinesSearchViewState extends State<_HeadlinesSearchView> {
126
132
enabledBorder: InputBorder .none,
127
133
// Add a subtle background fill
128
134
filled: true ,
129
- // Use a subtle color from the theme for the fill
135
+
130
136
fillColor: colorScheme.surface.withOpacity (0.1 ),
131
137
// Apply consistent padding using AppSpacing
132
138
contentPadding: const EdgeInsets .symmetric (
133
139
horizontal: AppSpacing .paddingMedium,
134
- vertical: AppSpacing .paddingSmall, // Adjust vertical padding as needed
140
+ vertical:
141
+ AppSpacing .paddingSmall, // Adjust vertical padding as needed
135
142
),
136
143
// Add a clear button that appears when text is entered
137
- suffixIcon: _showClearButton
138
- ? IconButton (
139
- icon: Icon (
140
- Icons .clear,
141
- // Use icon color appropriate for AppBar
142
- color: appBarTheme.iconTheme? .color ?? colorScheme.onSurface,
143
- ),
144
- onPressed: () {
145
- _textController.clear ();
146
- // Optionally trigger an empty search or reset state
147
- // _performSearch(); // Uncomment if clearing should trigger search
148
- },
149
- )
150
- : null , // No icon when text field is empty
144
+ suffixIcon:
145
+ _showClearButton
146
+ ? IconButton (
147
+ icon: Icon (
148
+ Icons .clear,
149
+
150
+ color:
151
+ appBarTheme.iconTheme? .color ??
152
+ colorScheme.onSurface,
153
+ ),
154
+ onPressed: _textController.clear,
155
+ )
156
+ : null , // No icon when text field is empty
151
157
),
152
158
// Trigger search on submit (e.g., pressing Enter on keyboard)
153
159
onSubmitted: (_) => _performSearch (),
@@ -157,7 +163,7 @@ class _HeadlinesSearchViewState extends State<_HeadlinesSearchView> {
157
163
IconButton (
158
164
icon: const Icon (Icons .search),
159
165
tooltip: l10n.headlinesSearchActionTooltip, // Re-added tooltip
160
- onPressed: _performSearch, // Use the dedicated search method
166
+ onPressed: _performSearch,
161
167
),
162
168
],
163
169
),
@@ -167,57 +173,57 @@ class _HeadlinesSearchViewState extends State<_HeadlinesSearchView> {
167
173
return switch (state) {
168
174
// Loading state
169
175
HeadlinesSearchLoading () => InitialStateWidget (
170
- icon: Icons .manage_search, // Changed icon
171
- headline: l10n.headlinesSearchInitialHeadline, // Keep initial text for loading phase
172
- subheadline: l10n.headlinesSearchInitialSubheadline,
173
- ),
176
+ icon: Icons .manage_search, // Changed icon
177
+ headline:
178
+ l10n.headlinesSearchInitialHeadline, // Keep initial text for loading phase
179
+ subheadline: l10n.headlinesSearchInitialSubheadline,
180
+ ),
174
181
// Success state with results
175
182
HeadlinesSearchSuccess (
176
183
: final headlines,
177
184
: final hasMore,
178
185
: final errorMessage, // Check for specific error message within success
179
- : final lastSearchTerm, // Use lastSearchTerm for retries
186
+ : final lastSearchTerm,
180
187
) =>
181
188
errorMessage != null
182
189
// Display error if present within success state
183
190
? FailureStateWidget (
184
- message: errorMessage,
185
- onRetry: () {
186
- // Retry with the last successful search term
187
- context.read <HeadlinesSearchBloc >().add (
188
- HeadlinesSearchFetchRequested (
189
- searchTerm: lastSearchTerm ?? '' ,
190
- ),
191
- );
192
- },
193
- )
191
+ message: errorMessage,
192
+ onRetry: () {
193
+ // Retry with the last successful search term
194
+ context.read <HeadlinesSearchBloc >().add (
195
+ HeadlinesSearchFetchRequested (
196
+ searchTerm: lastSearchTerm ?? '' ,
197
+ ),
198
+ );
199
+ },
200
+ )
194
201
// Display "no results" if list is empty
195
202
: headlines.isEmpty
196
- ? InitialStateWidget (
197
- icon: Icons .search_off,
198
- headline: l10n.headlinesSearchNoResultsHeadline,
199
- subheadline:
200
- l10n.headlinesSearchNoResultsSubheadline,
201
- )
202
- // Display the list of headlines
203
- : ListView .builder (
204
- controller: _scrollController,
205
- // Add 1 for loading indicator if more items exist
206
- itemCount:
207
- hasMore ? headlines.length + 1 : headlines.length,
208
- itemBuilder: (context, index) {
209
- // Show loading indicator at the end if hasMore
210
- if (index >= headlines.length) {
211
- // Ensure loading indicator is visible
212
- return const Padding (
213
- padding: EdgeInsets .all (AppSpacing .paddingLarge),
214
- child: Center (child: CircularProgressIndicator ()),
215
- );
216
- }
217
- // Display headline item
218
- return HeadlineItemWidget (headline: headlines[index]);
219
- },
220
- ),
203
+ ? InitialStateWidget (
204
+ icon: Icons .search_off,
205
+ headline: l10n.headlinesSearchNoResultsHeadline,
206
+ subheadline: l10n.headlinesSearchNoResultsSubheadline,
207
+ )
208
+ // Display the list of headlines
209
+ : ListView .builder (
210
+ controller: _scrollController,
211
+ // Add 1 for loading indicator if more items exist
212
+ itemCount:
213
+ hasMore ? headlines.length + 1 : headlines.length,
214
+ itemBuilder: (context, index) {
215
+ // Show loading indicator at the end if hasMore
216
+ if (index >= headlines.length) {
217
+ // Ensure loading indicator is visible
218
+ return const Padding (
219
+ padding: EdgeInsets .all (AppSpacing .paddingLarge),
220
+ child: Center (child: CircularProgressIndicator ()),
221
+ );
222
+ }
223
+ // Display headline item
224
+ return HeadlineItemWidget (headline: headlines[index]);
225
+ },
226
+ ),
221
227
// Default case (should ideally not be reached if states are handled)
222
228
_ => const SizedBox .shrink (),
223
229
};
0 commit comments