Skip to content

Commit 10a1a7d

Browse files
committed
refactor: move duplicated widgets into one file
1 parent 555af2d commit 10a1a7d

File tree

4 files changed

+199
-154
lines changed

4 files changed

+199
-154
lines changed

lib/screens/playlist_page.dart

Lines changed: 27 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ import 'package:musify/utilities/utils.dart';
4242
import 'package:musify/widgets/edit_playlist_dialog.dart';
4343
import 'package:musify/widgets/playlist_cube.dart';
4444
import 'package:musify/widgets/playlist_header.dart';
45+
import 'package:musify/widgets/playlists_page/playlist_search_bar.dart';
46+
import 'package:musify/widgets/playlists_page/shuffle_play_button.dart';
4547
import 'package:musify/widgets/song_bar.dart';
4648
import 'package:musify/widgets/sort_chips.dart';
4749
import 'package:musify/widgets/spinner.dart';
@@ -245,8 +247,20 @@ class _PlaylistPageState extends State<PlaylistPage> {
245247
spacing: 8,
246248
runSpacing: 8,
247249
children: [
248-
if (songsLength > 0) _buildPlayButton(primaryColor),
249-
if (songsLength > 0) _buildShufflePlayButton(primaryColor),
250+
if (songsLength > 0)
251+
IconButton.filled(
252+
icon: Icon(
253+
FluentIcons.play_24_filled,
254+
color: colorScheme.onPrimary,
255+
),
256+
iconSize: 24,
257+
onPressed: () => audioHandler.playPlaylistSong(
258+
playlist: _playlist,
259+
songIndex: 0,
260+
),
261+
),
262+
if (songsLength > 0)
263+
ShufflePlayButton(songs: _playlist['list'] as List? ?? []),
250264
if (widget.playlistId != null && !isUserCreated)
251265
_buildLikeButton(primaryColor),
252266
_buildSyncButton(primaryColor),
@@ -275,85 +289,23 @@ class _PlaylistPageState extends State<PlaylistPage> {
275289
],
276290
if (songsLength > 0) ...[
277291
const SizedBox(height: 16),
278-
_buildSearchBar(colorScheme),
292+
PlaylistSearchBar(
293+
query: _searchQuery,
294+
onChanged: (value) {
295+
setState(() => _searchQuery = value);
296+
_pagingController.refresh();
297+
},
298+
onCleared: () {
299+
setState(() => _searchQuery = '');
300+
_pagingController.refresh();
301+
},
302+
),
279303
],
280304
const SizedBox(height: 16),
281305
],
282306
);
283307
}
284308

285-
Widget _buildSearchBar(ColorScheme colorScheme) {
286-
return Padding(
287-
padding: const EdgeInsets.symmetric(horizontal: 16),
288-
child: SearchBar(
289-
hintText: context.l10n!.search,
290-
leading: Icon(
291-
Icons.search_rounded,
292-
color: colorScheme.onSurfaceVariant,
293-
),
294-
trailing: [
295-
if (_searchQuery.isNotEmpty)
296-
IconButton(
297-
icon: const Icon(Icons.close_rounded),
298-
onPressed: () {
299-
setState(() => _searchQuery = '');
300-
_pagingController.refresh();
301-
},
302-
),
303-
],
304-
onChanged: (value) {
305-
setState(() => _searchQuery = value);
306-
_pagingController.refresh();
307-
},
308-
elevation: const WidgetStatePropertyAll(0),
309-
backgroundColor: WidgetStatePropertyAll(
310-
colorScheme.surfaceContainerHigh,
311-
),
312-
shape: WidgetStatePropertyAll(
313-
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
314-
),
315-
padding: const WidgetStatePropertyAll(
316-
EdgeInsets.symmetric(horizontal: 12),
317-
),
318-
),
319-
);
320-
}
321-
322-
Widget _buildPlayButton(Color primaryColor) {
323-
return IconButton.filled(
324-
icon: Icon(
325-
FluentIcons.play_24_filled,
326-
color: Theme.of(context).colorScheme.onPrimary,
327-
),
328-
iconSize: 24,
329-
onPressed: () =>
330-
audioHandler.playPlaylistSong(playlist: _playlist, songIndex: 0),
331-
);
332-
}
333-
334-
Widget _buildShufflePlayButton(Color primaryColor) {
335-
return IconButton.filledTonal(
336-
icon: Icon(FluentIcons.arrow_shuffle_24_filled, color: primaryColor),
337-
iconSize: 24,
338-
tooltip: 'Shuffle play',
339-
onPressed: () async {
340-
final playlistSongs = _playlist['list'] as List<dynamic>?;
341-
if (playlistSongs == null || playlistSongs.isEmpty) return;
342-
343-
final shuffledSongs = List<Map>.from(playlistSongs.whereType<Map>());
344-
if (shuffledSongs.isEmpty) return;
345-
346-
shuffledSongs.shuffle();
347-
348-
await audioHandler.addPlaylistToQueue(
349-
shuffledSongs,
350-
replace: true,
351-
startIndex: 0,
352-
);
353-
},
354-
);
355-
}
356-
357309
Widget _buildShareButton(Color primaryColor) {
358310
return IconButton.filledTonal(
359311
icon: Icon(FluentIcons.share_24_regular, color: primaryColor),

lib/screens/user_songs_page.dart

Lines changed: 35 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import 'package:musify/utilities/flutter_toast.dart';
3030
import 'package:musify/utilities/utils.dart';
3131
import 'package:musify/widgets/playlist_cube.dart';
3232
import 'package:musify/widgets/playlist_header.dart';
33+
import 'package:musify/widgets/playlists_page/playlist_search_bar.dart';
34+
import 'package:musify/widgets/playlists_page/shuffle_play_button.dart';
3335
import 'package:musify/widgets/song_bar.dart';
3436
import 'package:musify/widgets/sort_chips.dart';
3537

@@ -177,7 +179,8 @@ class _UserSongsPageState extends State<UserSongsPage> {
177179
int songsLength,
178180
bool isOfflineSongs,
179181
) {
180-
final primaryColor = Theme.of(context).colorScheme.primary;
182+
final colorScheme = Theme.of(context).colorScheme;
183+
final primaryColor = colorScheme.primary;
181184
final isRecentlyPlayed = title == context.l10n!.recentlyPlayed;
182185

183186
return Column(
@@ -188,8 +191,29 @@ class _UserSongsPageState extends State<UserSongsPage> {
188191
spacing: 8,
189192
runSpacing: 8,
190193
children: [
191-
if (songsLength > 0) _buildPlayButton(primaryColor, title),
192-
if (songsLength > 0) _buildShufflePlayButton(primaryColor),
194+
if (songsLength > 0)
195+
IconButton.filled(
196+
icon: Icon(
197+
FluentIcons.play_24_filled,
198+
color: colorScheme.onPrimary,
199+
),
200+
iconSize: 24,
201+
onPressed: () {
202+
final songsList = getSongsList(widget.page);
203+
final playlist = {
204+
'ytid': '',
205+
'title': title,
206+
'source': 'user-created',
207+
'list': songsList,
208+
};
209+
audioHandler.playPlaylistSong(
210+
playlist: playlist,
211+
songIndex: 0,
212+
);
213+
},
214+
),
215+
if (songsLength > 0)
216+
ShufflePlayButton(songs: getSongsList(widget.page)),
193217
if (isRecentlyPlayed && songsLength > 0)
194218
_buildClearRecentsButton(primaryColor),
195219
],
@@ -209,44 +233,19 @@ class _UserSongsPageState extends State<UserSongsPage> {
209233
},
210234
),
211235
],
212-
if (songsLength > 0) ...[const SizedBox(height: 16), _buildSearchBar()],
236+
if (songsLength > 0) ...[
237+
const SizedBox(height: 16),
238+
PlaylistSearchBar(
239+
query: _searchQuery,
240+
onChanged: (value) => setState(() => _searchQuery = value),
241+
onCleared: () => setState(() => _searchQuery = ''),
242+
),
243+
],
213244
const SizedBox(height: 16),
214245
],
215246
);
216247
}
217248

218-
Widget _buildSearchBar() {
219-
final colorScheme = Theme.of(context).colorScheme;
220-
return Padding(
221-
padding: const EdgeInsets.symmetric(horizontal: 16),
222-
child: SearchBar(
223-
hintText: context.l10n!.search,
224-
leading: Icon(
225-
Icons.search_rounded,
226-
color: colorScheme.onSurfaceVariant,
227-
),
228-
trailing: [
229-
if (_searchQuery.isNotEmpty)
230-
IconButton(
231-
icon: const Icon(Icons.close_rounded),
232-
onPressed: () => setState(() => _searchQuery = ''),
233-
),
234-
],
235-
onChanged: (value) => setState(() => _searchQuery = value),
236-
elevation: const WidgetStatePropertyAll(0),
237-
backgroundColor: WidgetStatePropertyAll(
238-
colorScheme.surfaceContainerHigh,
239-
),
240-
shape: WidgetStatePropertyAll(
241-
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
242-
),
243-
padding: const WidgetStatePropertyAll(
244-
EdgeInsets.symmetric(horizontal: 12),
245-
),
246-
),
247-
);
248-
}
249-
250249
Widget _buildPlaylistImage(String title, IconData icon) {
251250
final screenWidth = MediaQuery.sizeOf(context).width;
252251
final isLandscape = screenWidth > MediaQuery.sizeOf(context).height;
@@ -257,49 +256,6 @@ class _UserSongsPageState extends State<UserSongsPage> {
257256
);
258257
}
259258

260-
Widget _buildPlayButton(Color primaryColor, String title) {
261-
final songsList = getSongsList(widget.page);
262-
final playlist = {
263-
'ytid': '',
264-
'title': title,
265-
'source': 'user-created',
266-
'list': songsList,
267-
};
268-
269-
return IconButton.filled(
270-
icon: Icon(
271-
FluentIcons.play_24_filled,
272-
color: Theme.of(context).colorScheme.onPrimary,
273-
),
274-
iconSize: 24,
275-
onPressed: () =>
276-
audioHandler.playPlaylistSong(playlist: playlist, songIndex: 0),
277-
);
278-
}
279-
280-
Widget _buildShufflePlayButton(Color primaryColor) {
281-
return IconButton.filledTonal(
282-
icon: Icon(FluentIcons.arrow_shuffle_24_filled, color: primaryColor),
283-
iconSize: 24,
284-
tooltip: 'Shuffle play',
285-
onPressed: () async {
286-
final songsList = getSongsList(widget.page);
287-
if (songsList.isEmpty) return;
288-
289-
final shuffledSongs = List<Map>.from(songsList.whereType<Map>());
290-
if (shuffledSongs.isEmpty) return;
291-
292-
shuffledSongs.shuffle();
293-
294-
await audioHandler.addPlaylistToQueue(
295-
shuffledSongs,
296-
replace: true,
297-
startIndex: 0,
298-
);
299-
},
300-
);
301-
}
302-
303259
Widget _buildClearRecentsButton(Color primaryColor) {
304260
return IconButton.filledTonal(
305261
icon: Icon(FluentIcons.delete_24_regular, color: primaryColor),
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (C) 2026 Valeri Gokadze
3+
*
4+
* Musify is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* Musify is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*
17+
*
18+
* For more information about Musify, including how to contribute,
19+
* please visit: https://github.com/gokadzev/Musify
20+
*/
21+
22+
import 'package:flutter/material.dart';
23+
import 'package:musify/extensions/l10n.dart';
24+
25+
class PlaylistSearchBar extends StatefulWidget {
26+
const PlaylistSearchBar({
27+
super.key,
28+
required this.query,
29+
required this.onChanged,
30+
required this.onCleared,
31+
});
32+
33+
final String query;
34+
final ValueChanged<String> onChanged;
35+
final VoidCallback onCleared;
36+
37+
@override
38+
State<PlaylistSearchBar> createState() => _PlaylistSearchBarState();
39+
}
40+
41+
class _PlaylistSearchBarState extends State<PlaylistSearchBar> {
42+
final _controller = SearchController();
43+
44+
@override
45+
void dispose() {
46+
_controller.dispose();
47+
super.dispose();
48+
}
49+
50+
@override
51+
Widget build(BuildContext context) {
52+
final colorScheme = Theme.of(context).colorScheme;
53+
return Padding(
54+
padding: const EdgeInsets.symmetric(horizontal: 16),
55+
child: SearchBar(
56+
controller: _controller,
57+
hintText: context.l10n!.search,
58+
leading: Icon(
59+
Icons.search_rounded,
60+
color: colorScheme.onSurfaceVariant,
61+
),
62+
trailing: [
63+
if (widget.query.isNotEmpty)
64+
IconButton(
65+
icon: const Icon(Icons.close_rounded),
66+
onPressed: () {
67+
_controller.clear();
68+
widget.onCleared();
69+
},
70+
),
71+
],
72+
onChanged: widget.onChanged,
73+
elevation: const WidgetStatePropertyAll(0),
74+
backgroundColor: WidgetStatePropertyAll(
75+
colorScheme.surfaceContainerHigh,
76+
),
77+
shape: WidgetStatePropertyAll(
78+
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
79+
),
80+
padding: const WidgetStatePropertyAll(
81+
EdgeInsets.symmetric(horizontal: 12),
82+
),
83+
),
84+
);
85+
}
86+
}

0 commit comments

Comments
 (0)