|
| 1 | +import 'package:flutter/foundation.dart' show kIsWeb; // Added |
1 | 2 | import 'package:flutter/material.dart';
|
2 | 3 | import 'package:flutter_bloc/flutter_bloc.dart';
|
3 | 4 | import 'package:go_router/go_router.dart'; // Added
|
@@ -145,81 +146,116 @@ class _EntityDetailsViewState extends State<EntityDetailsView> {
|
145 | 146 | ? (state.entity as Source).description
|
146 | 147 | : null;
|
147 | 148 |
|
148 |
| - final String? entityIconUrl = (state.entity is Category && (state.entity as Category).iconUrl != null) |
| 149 | + final String? entityIconUrl = (state.entity is Category && |
| 150 | + (state.entity as Category).iconUrl != null) |
149 | 151 | ? (state.entity as Category).iconUrl
|
150 |
| - : null; // Source model does not have iconUrl |
| 152 | + : null; |
| 153 | + |
| 154 | + final followButton = IconButton( |
| 155 | + icon: Icon( |
| 156 | + state.isFollowing |
| 157 | + ? Icons.check_circle // Filled when following |
| 158 | + : Icons.add_circle_outline, |
| 159 | + color: state.isFollowing |
| 160 | + ? theme.colorScheme.primary |
| 161 | + : theme.colorScheme.onSurfaceVariant, |
| 162 | + ), |
| 163 | + tooltip: state.isFollowing |
| 164 | + ? l10n.unfollowButtonLabel |
| 165 | + : l10n.followButtonLabel, |
| 166 | + onPressed: () { |
| 167 | + context |
| 168 | + .read<EntityDetailsBloc>() |
| 169 | + .add(const EntityDetailsToggleFollowRequested()); |
| 170 | + }, |
| 171 | + ); |
| 172 | + |
| 173 | + final Widget appBarTitleWidget = Row( |
| 174 | + mainAxisSize: MainAxisSize.min, |
| 175 | + children: [ |
| 176 | + if (entityIconUrl != null) |
| 177 | + Padding( |
| 178 | + padding: const EdgeInsets.only(right: AppSpacing.sm), |
| 179 | + child: ClipRRect( |
| 180 | + borderRadius: BorderRadius.circular(AppSpacing.xs), |
| 181 | + child: Image.network( |
| 182 | + entityIconUrl, |
| 183 | + width: kToolbarHeight - 16, // AppBar height minus padding |
| 184 | + height: kToolbarHeight - 16, |
| 185 | + fit: BoxFit.cover, |
| 186 | + errorBuilder: (context, error, stackTrace) => |
| 187 | + const Icon(Icons.category_outlined, size: kToolbarHeight - 20), |
| 188 | + ), |
| 189 | + ), |
| 190 | + ) |
| 191 | + else if (state.entityType == EntityType.category) |
| 192 | + Padding( |
| 193 | + padding: const EdgeInsets.only(right: AppSpacing.sm), |
| 194 | + child: Icon(Icons.category_outlined, size: kToolbarHeight - 20, color: theme.colorScheme.onSurface), |
| 195 | + ) |
| 196 | + else if (state.entityType == EntityType.source) |
| 197 | + Padding( |
| 198 | + padding: const EdgeInsets.only(right: AppSpacing.sm), |
| 199 | + child: Icon(Icons.source_outlined, size: kToolbarHeight - 20, color: theme.colorScheme.onSurface), |
| 200 | + ), |
| 201 | + Flexible( |
| 202 | + child: Text( |
| 203 | + appBarTitle, |
| 204 | + overflow: TextOverflow.ellipsis, |
| 205 | + ), |
| 206 | + ), |
| 207 | + if (description != null && description.isNotEmpty) |
| 208 | + Tooltip( |
| 209 | + message: description, |
| 210 | + child: IconButton( |
| 211 | + icon: Icon(Icons.info_outline, color: theme.colorScheme.onSurfaceVariant), |
| 212 | + onPressed: () { |
| 213 | + // On mobile, show dialog for description |
| 214 | + if (!kIsWeb) { // kIsWeb can be used to differentiate behavior |
| 215 | + showDialog<void>( |
| 216 | + context: context, |
| 217 | + builder: (BuildContext dialogContext) { |
| 218 | + return AlertDialog( |
| 219 | + title: Text(appBarTitle), |
| 220 | + content: SingleChildScrollView(child: Text(description)), |
| 221 | + actions: <Widget>[ |
| 222 | + TextButton( |
| 223 | + child: Text(MaterialLocalizations.of(dialogContext).closeButtonLabel), |
| 224 | + onPressed: () { |
| 225 | + Navigator.of(dialogContext).pop(); |
| 226 | + }, |
| 227 | + ), |
| 228 | + ], |
| 229 | + ); |
| 230 | + }, |
| 231 | + ); |
| 232 | + } |
| 233 | + }, |
| 234 | + ), |
| 235 | + ), |
| 236 | + ], |
| 237 | + ); |
151 | 238 |
|
152 | 239 | return CustomScrollView(
|
153 | 240 | controller: _scrollController,
|
154 | 241 | slivers: [
|
155 | 242 | SliverAppBar(
|
156 |
| - title: Text(appBarTitle), |
| 243 | + title: appBarTitleWidget, |
157 | 244 | pinned: true,
|
158 |
| - expandedHeight: entityIconUrl != null ? 200.0 : kToolbarHeight, |
159 |
| - flexibleSpace: entityIconUrl != null |
160 |
| - ? FlexibleSpaceBar( |
161 |
| - background: Image.network( |
162 |
| - entityIconUrl, |
163 |
| - fit: BoxFit.cover, |
164 |
| - errorBuilder: (context, error, stackTrace) => |
165 |
| - const Icon(Icons.image_not_supported_outlined, size: 48), |
166 |
| - ), |
167 |
| - ) |
168 |
| - : null, |
| 245 | + actions: [followButton], |
| 246 | + // Removed expandedHeight and flexibleSpace for a standard AppBar |
169 | 247 | ),
|
170 |
| - SliverToBoxAdapter( |
| 248 | + SliverToBoxAdapter( // This adapter is now just for spacing and the section title/divider |
171 | 249 | child: Padding(
|
172 |
| - padding: const EdgeInsets.all(AppSpacing.paddingMedium), |
| 250 | + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.paddingMedium) |
| 251 | + .copyWith(top: AppSpacing.lg), // Add top padding |
173 | 252 | child: Column(
|
174 |
| - crossAxisAlignment: CrossAxisAlignment.start, |
| 253 | + crossAxisAlignment: CrossAxisAlignment.start, |
175 | 254 | children: [
|
176 |
| - Row( |
177 |
| - mainAxisAlignment: MainAxisAlignment.spaceBetween, |
178 |
| - crossAxisAlignment: CrossAxisAlignment.start, |
179 |
| - children: [ |
180 |
| - Expanded( |
181 |
| - child: Text( |
182 |
| - appBarTitle, |
183 |
| - style: theme.textTheme.headlineMedium, |
184 |
| - ), |
185 |
| - ), |
186 |
| - const SizedBox(width: AppSpacing.md), |
187 |
| - ElevatedButton.icon( |
188 |
| - icon: Icon( |
189 |
| - state.isFollowing |
190 |
| - ? Icons.check_circle // Filled when following |
191 |
| - : Icons.add_circle_outline, |
192 |
| - ), |
193 |
| - label: Text( |
194 |
| - state.isFollowing |
195 |
| - ? l10n.unfollowButtonLabel |
196 |
| - : l10n.followButtonLabel, |
197 |
| - ), |
198 |
| - style: ElevatedButton.styleFrom( |
199 |
| - backgroundColor: state.isFollowing |
200 |
| - ? theme.colorScheme.secondaryContainer |
201 |
| - : theme.colorScheme.primaryContainer, |
202 |
| - foregroundColor: state.isFollowing |
203 |
| - ? theme.colorScheme.onSecondaryContainer |
204 |
| - : theme.colorScheme.onPrimaryContainer, |
205 |
| - ), |
206 |
| - onPressed: () { |
207 |
| - context |
208 |
| - .read<EntityDetailsBloc>() |
209 |
| - .add(const EntityDetailsToggleFollowRequested()); |
210 |
| - }, |
211 |
| - ), |
212 |
| - ], |
213 |
| - ), |
214 |
| - if (description != null && description.isNotEmpty) ...[ |
215 |
| - const SizedBox(height: AppSpacing.md), |
216 |
| - Text(description, style: theme.textTheme.bodyMedium), |
217 |
| - ], |
218 |
| - const SizedBox(height: AppSpacing.lg), // Increased spacing |
219 |
| - if (state.headlines.isNotEmpty || state.headlinesStatus == EntityHeadlinesStatus.loadingMore) |
220 |
| - Text(l10n.headlinesSectionTitle, style: theme.textTheme.titleLarge), // Section title |
221 |
| - if (state.headlines.isNotEmpty || state.headlinesStatus == EntityHeadlinesStatus.loadingMore) |
| 255 | + if (state.headlines.isNotEmpty || state.headlinesStatus == EntityHeadlinesStatus.loadingMore) ...[ |
| 256 | + Text(l10n.headlinesSectionTitle, style: theme.textTheme.titleLarge), |
222 | 257 | const Divider(height: AppSpacing.md),
|
| 258 | + ] |
223 | 259 | ],
|
224 | 260 | ),
|
225 | 261 | ),
|
|
0 commit comments