Skip to content

Commit 3a61d2e

Browse files
committed
refactor: update playlist header layout and styling, refine track card typography, and update default profile image state
1 parent 0a4d0bd commit 3a61d2e

19 files changed

Lines changed: 701 additions & 376 deletions

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ migrate_working_dir/
4040
/.roo/
4141
/.trae/
4242
/.windsurf/
43-
43+
/.mcp.json
44+
/.opencode.json
4445
# Xcode
4546
*.xcworkspace/xcuserdata/
4647
*.xcodeproj/xcuserdata/
@@ -126,3 +127,5 @@ downloads/
126127

127128
# ── Pub lock (keep for apps, remove for packages) ─
128129
# pubspec.lock ← Keep committed for apps (it is committed here)
130+
# Added by code-review-graph
131+
.code-review-graph/

lib/providers/profile_provider.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,12 @@ class ProfileImageNotifier extends StateNotifier<ProfileImageState> {
126126
final preferences = await SharedPreferences.getInstance();
127127
await preferences.setString(
128128
_profileImageModeKey,
129-
ProfileImageMode.none.name,
129+
ProfileImageMode.defaultAsset.name,
130130
);
131131
await preferences.remove(_profileImagePathKey);
132132

133133
state = const ProfileImageState(
134-
mode: ProfileImageMode.none,
134+
mode: ProfileImageMode.defaultAsset,
135135
);
136136
}
137137

lib/screens/downloads_screen.dart

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,19 @@ class DownloadsScreen extends ConsumerWidget {
2323
appBar: AppBar(
2424
backgroundColor: bgBase,
2525
elevation: 0,
26-
title: Text('DOWNLOADS', style: Theme.of(context).textTheme.displayLarge),
26+
title: LayoutBuilder(
27+
builder: (context, constraints) {
28+
final screenWidth = MediaQuery.of(context).size.width;
29+
final fontSize = (screenWidth * 0.056).clamp(18.0, 24.0);
30+
return Text(
31+
'DOWNLOADS',
32+
style: Theme.of(context).textTheme.displayLarge?.copyWith(
33+
fontSize: fontSize,
34+
letterSpacing: 1.2,
35+
),
36+
);
37+
},
38+
),
2739
),
2840
body: Column(
2941
children: [
@@ -40,6 +52,7 @@ class DownloadsScreen extends ConsumerWidget {
4052
context,
4153
ref,
4254
download,
55+
_computeItemHeight(constraints.maxHeight, activeDownloads.length, completedDownloads.length),
4356
),
4457
),
4558
],
@@ -50,7 +63,7 @@ class DownloadsScreen extends ConsumerWidget {
5063
...completedDownloads.map<Widget>(
5164
(download) => _CompletedDownloadTile(
5265
download: download,
53-
height: _computeItemHeight(constraints.maxHeight, completedDownloads.length),
66+
height: _computeItemHeight(constraints.maxHeight, activeDownloads.length, completedDownloads.length),
5467
),
5568
),
5669
],
@@ -64,21 +77,31 @@ class DownloadsScreen extends ConsumerWidget {
6477
);
6578
}
6679

67-
/// Compute item height to fit ~11 items, adapting to screen size.
68-
double _computeItemHeight(double availableHeight, int itemCount) {
69-
// Reserve space for section header (~28px)
70-
final listHeight = availableHeight - 28;
71-
// Target 11 items visible, min 48px per item
72-
final targetHeight = (listHeight / 11).clamp(48.0, 72.0);
80+
/// Compute item height to fit ~8.5 items, making elements look "zoomed in".
81+
double _computeItemHeight(double availableHeight, int activeCount, int completedCount) {
82+
// Reserve space for section headers (~44px each now with padding)
83+
double reservedHeight = 0;
84+
if (activeCount > 0) reservedHeight += 44;
85+
if (completedCount >= 0) reservedHeight += 44;
86+
87+
final listHeight = (availableHeight - reservedHeight).clamp(0.0, availableHeight);
88+
89+
// Target 8.5 items visible for a larger, "zoomed" look
90+
final targetHeight = (listHeight / 8.5).clamp(64.0, 92.0);
7391
return targetHeight;
7492
}
7593

7694
Widget _buildSectionHeader(String title) {
7795
return Padding(
78-
padding: const EdgeInsets.fromLTRB(16, 12, 16, 4),
96+
padding: const EdgeInsets.fromLTRB(16, 20, 16, 10),
7997
child: Text(
8098
title,
81-
style: const TextStyle(color: textSecondary, fontSize: 10, fontWeight: FontWeight.bold, letterSpacing: 0.12),
99+
style: const TextStyle(
100+
color: textSecondary,
101+
fontSize: 14,
102+
fontWeight: FontWeight.bold,
103+
letterSpacing: 1.2,
104+
),
82105
),
83106
);
84107
}
@@ -102,33 +125,35 @@ class DownloadsScreen extends ConsumerWidget {
102125
BuildContext context,
103126
WidgetRef ref,
104127
DownloadRecord download,
128+
double height,
105129
) {
106130
return Container(
131+
height: height,
107132
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 2),
108-
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
133+
padding: const EdgeInsets.symmetric(horizontal: 16),
109134
color: bgCard,
110135
child: Row(
111136
children: [
112-
_TrackArtwork(thumbnailUrl: download.thumbnailUrl, size: 36),
113-
const SizedBox(width: 10),
137+
_TrackArtwork(thumbnailUrl: download.thumbnailUrl, size: (height * 0.72).clamp(44.0, 64.0)),
138+
const SizedBox(width: 14),
114139
Expanded(
115140
child: Column(
116141
crossAxisAlignment: CrossAxisAlignment.start,
117-
mainAxisSize: MainAxisSize.min,
142+
mainAxisAlignment: MainAxisAlignment.center,
118143
children: [
119144
Text(
120145
download.title,
121-
style: const TextStyle(color: textPrimary, fontSize: 12, fontWeight: FontWeight.w500),
146+
style: const TextStyle(color: textPrimary, fontSize: 15, fontWeight: FontWeight.bold),
122147
maxLines: 1,
123148
overflow: TextOverflow.ellipsis,
124149
),
125-
const SizedBox(height: 2),
150+
const SizedBox(height: 4),
126151
// Progress bar inline
127152
Row(
128153
children: [
129154
Expanded(
130155
child: Container(
131-
height: 2,
156+
height: 3,
132157
color: bgDivider,
133158
alignment: Alignment.centerLeft,
134159
child: FractionallySizedBox(
@@ -137,22 +162,22 @@ class DownloadsScreen extends ConsumerWidget {
137162
),
138163
),
139164
),
140-
const SizedBox(width: 8),
165+
const SizedBox(width: 10),
141166
Text(
142167
'${(download.progress * 100).round()}%',
143-
style: const TextStyle(color: accentPrimary, fontSize: 9, fontWeight: FontWeight.bold),
168+
style: const TextStyle(color: accentPrimary, fontSize: 12, fontWeight: FontWeight.bold),
144169
),
145170
],
146171
),
147172
],
148173
),
149174
),
150-
const SizedBox(width: 8),
175+
const SizedBox(width: 10),
151176
GestureDetector(
152177
onTap: () {
153178
ref.read(downloadNotifierProvider.notifier).cancelDownload(download.videoId);
154179
},
155-
child: const Icon(Icons.close_rounded, color: accentRed, size: 18),
180+
child: const Icon(Icons.close_rounded, color: accentRed, size: 24),
156181
),
157182
],
158183
),
@@ -177,7 +202,6 @@ class DownloadsScreen extends ConsumerWidget {
177202
/// Completed download tile with swipe gestures and tap-to-play.
178203
class _CompletedDownloadTile extends ConsumerStatefulWidget {
179204
const _CompletedDownloadTile({
180-
super.key,
181205
required this.download,
182206
required this.height,
183207
});
@@ -259,14 +283,14 @@ class _CompletedDownloadTileState extends ConsumerState<_CompletedDownloadTile>
259283
@override
260284
Widget build(BuildContext context) {
261285
final width = MediaQuery.of(context).size.width;
262-
final thumbSize = (widget.height * 0.7).clamp(28.0, 48.0);
286+
final thumbSize = (widget.height * 0.72).clamp(44.0, 64.0);
263287

264288
return Stack(
265289
children: [
266290
// Background layer containing actions
267291
Positioned.fill(
268292
child: Container(
269-
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 1),
293+
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 2),
270294
decoration: BoxDecoration(
271295
color: bgCard,
272296
borderRadius: BorderRadius.circular(2),
@@ -279,14 +303,14 @@ class _CompletedDownloadTileState extends ConsumerState<_CompletedDownloadTile>
279303
width: width * _swipeLimitPercent,
280304
color: accentPrimary.withValues(alpha: 0.15),
281305
alignment: Alignment.center,
282-
child: const Icon(Icons.queue_music_rounded, color: accentPrimary, size: 18),
306+
child: const Icon(Icons.queue_music_rounded, color: accentPrimary, size: 24),
283307
),
284308
// Right action (visible on left swipe)
285309
Container(
286310
width: width * _swipeLimitPercent,
287311
color: accentRed.withValues(alpha: 0.15),
288312
alignment: Alignment.center,
289-
child: const Icon(Icons.delete_outline_rounded, color: accentRed, size: 18),
313+
child: const Icon(Icons.delete_outline_rounded, color: accentRed, size: 24),
290314
),
291315
],
292316
),
@@ -310,35 +334,36 @@ class _CompletedDownloadTileState extends ConsumerState<_CompletedDownloadTile>
310334
offset: Offset(_dragOffset, 0),
311335
child: Container(
312336
height: widget.height,
313-
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 1),
314-
padding: const EdgeInsets.symmetric(horizontal: 12),
337+
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 2),
338+
padding: const EdgeInsets.symmetric(horizontal: 16),
315339
color: bgCard,
316340
child: Row(
317341
children: [
318342
_TrackArtwork(thumbnailUrl: widget.download.thumbnailUrl, size: thumbSize),
319-
const SizedBox(width: 10),
343+
const SizedBox(width: 14),
320344
Expanded(
321345
child: Column(
322346
crossAxisAlignment: CrossAxisAlignment.start,
323347
mainAxisAlignment: MainAxisAlignment.center,
324348
children: [
325349
Text(
326350
widget.download.title,
327-
style: const TextStyle(color: textPrimary, fontSize: 12, fontWeight: FontWeight.w500),
351+
style: const TextStyle(color: textPrimary, fontSize: 15, fontWeight: FontWeight.bold),
328352
maxLines: 1,
329353
overflow: TextOverflow.ellipsis,
330354
),
355+
const SizedBox(height: 2),
331356
Text(
332357
widget.download.artist,
333-
style: const TextStyle(color: textSecondary, fontSize: 10),
358+
style: const TextStyle(color: textSecondary, fontSize: 12),
334359
maxLines: 1,
335360
overflow: TextOverflow.ellipsis,
336361
),
337362
],
338363
),
339364
),
340-
const SizedBox(width: 8),
341-
const Icon(Icons.check_circle_rounded, color: accentPrimary, size: 14),
365+
const SizedBox(width: 10),
366+
const Icon(Icons.check_circle_rounded, color: accentPrimary, size: 20),
342367
],
343368
),
344369
),

lib/screens/home_screen/home_collection_grid.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ class HomeCollectionGrid extends StatelessWidget {
1616
final visibleItems = items.take(8).toList();
1717
final width = MediaQuery.of(context).size.width - 32;
1818
final isCompact = width < 380;
19-
final bannerAspectRatio = isCompact ? 2.8 : 3.2;
19+
// Current aspect ratio results in 4 rows taking space of 4 rows.
20+
// To make 4 rows take space of 3 rows, we increase aspect ratio by 4/3.
21+
final bannerAspectRatio = isCompact ? (2.8 * 4 / 3) : (3.2 * 4 / 3);
2022

2123
return GridView.builder(
2224
shrinkWrap: true,
@@ -25,7 +27,7 @@ class HomeCollectionGrid extends StatelessWidget {
2527
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
2628
crossAxisCount: 2,
2729
crossAxisSpacing: 8,
28-
mainAxisSpacing: 8,
30+
mainAxisSpacing: 6, // Slightly reduced spacing
2931
childAspectRatio: bannerAspectRatio,
3032
),
3133
itemBuilder: (context, index) {

lib/screens/home_screen/home_screen.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@ class HomeScreen extends ConsumerWidget {
2121
super.key,
2222
required this.onViewMore,
2323
required this.onOpenMenu,
24+
required this.onDownloadsTap,
25+
required this.onSettingsTap,
2426
});
2527

2628
final VoidCallback onViewMore;
2729
final VoidCallback onOpenMenu;
30+
final VoidCallback onDownloadsTap;
31+
final VoidCallback onSettingsTap;
2832

2933
@override
3034
Widget build(BuildContext context, WidgetRef ref) {
@@ -62,6 +66,8 @@ class HomeScreen extends ConsumerWidget {
6266
final children = <Widget>[
6367
HomeTopBar(
6468
onProfileTap: onOpenMenu,
69+
onDownloadsTap: onDownloadsTap,
70+
onSettingsTap: onSettingsTap,
6571
displayName: displayName,
6672
profileImage: profileImage,
6773
),

lib/screens/home_screen/home_top_bar.dart

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,22 @@ class HomeTopBar extends StatelessWidget {
88
const HomeTopBar({
99
super.key,
1010
required this.onProfileTap,
11+
required this.onDownloadsTap,
12+
required this.onSettingsTap,
1113
required this.displayName,
1214
required this.profileImage,
1315
});
1416

1517
final VoidCallback onProfileTap;
18+
final VoidCallback onDownloadsTap;
19+
final VoidCallback onSettingsTap;
1620
final String displayName;
1721
final ProfileImageState profileImage;
1822

1923
@override
2024
Widget build(BuildContext context) {
2125
return Container(
22-
padding: const EdgeInsets.fromLTRB(16, 14, 16, 2),
26+
padding: const EdgeInsets.fromLTRB(16, 14, 8, 2),
2327
decoration: BoxDecoration(
2428
gradient: LinearGradient(
2529
begin: Alignment.topCenter,
@@ -68,8 +72,23 @@ class HomeTopBar extends StatelessWidget {
6872
fontSize: 22,
6973
letterSpacing: 0,
7074
),
75+
maxLines: 1,
76+
overflow: TextOverflow.ellipsis,
7177
),
7278
),
79+
const SizedBox(width: 4),
80+
IconButton(
81+
onPressed: onDownloadsTap,
82+
icon: const Icon(Icons.download_rounded, size: 24),
83+
color: textPrimary,
84+
tooltip: 'Downloads',
85+
),
86+
IconButton(
87+
onPressed: onSettingsTap,
88+
icon: const Icon(Icons.tune_rounded, size: 24),
89+
color: textPrimary,
90+
tooltip: 'Settings',
91+
),
7392
],
7493
),
7594
);

0 commit comments

Comments
 (0)