♻️ Update the icon set

This commit is contained in:
2025-12-20 12:24:34 +08:00
parent d07edbda78
commit 9a891a9a98
14 changed files with 132 additions and 127 deletions

View File

@@ -5,6 +5,7 @@ import 'package:groovybox/providers/audio_provider.dart';
import 'package:groovybox/ui/widgets/track_tile.dart'; import 'package:groovybox/ui/widgets/track_tile.dart';
import 'package:groovybox/ui/widgets/universal_image.dart'; import 'package:groovybox/ui/widgets/universal_image.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
class AlbumDetailScreen extends HookConsumerWidget { class AlbumDetailScreen extends HookConsumerWidget {
@@ -30,7 +31,7 @@ class AlbumDetailScreen extends HookConsumerWidget {
: Container( : Container(
color: Colors.grey[800], color: Colors.grey[800],
child: const Icon( child: const Icon(
Icons.album, Symbols.album,
size: 100, size: 100,
color: Colors.white54, color: Colors.white54,
), ),
@@ -66,7 +67,7 @@ class AlbumDetailScreen extends HookConsumerWidget {
onPressed: () { onPressed: () {
_playAlbum(ref, tracks); _playAlbum(ref, tracks);
}, },
icon: const Icon(Icons.play_arrow), icon: const Icon(Symbols.play_arrow),
label: const Text('Play All'), label: const Text('Play All'),
), ),
), ),

View File

@@ -14,6 +14,7 @@ import 'package:groovybox/ui/tabs/albums_tab.dart';
import 'package:groovybox/ui/tabs/playlists_tab.dart'; import 'package:groovybox/ui/tabs/playlists_tab.dart';
import 'package:groovybox/ui/widgets/track_tile.dart'; import 'package:groovybox/ui/widgets/track_tile.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -75,14 +76,14 @@ class LibraryScreen extends HookConsumerWidget {
appBar: isSelectionMode appBar: isSelectionMode
? AppBar( ? AppBar(
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.close), icon: const Icon(Symbols.close),
onPressed: clearSelection, onPressed: clearSelection,
), ),
title: Text('${selectedTrackIds.value.length} selected'), title: Text('${selectedTrackIds.value.length} selected'),
backgroundColor: Theme.of(context).primaryColorDark, backgroundColor: Theme.of(context).primaryColorDark,
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Icons.playlist_add), icon: const Icon(Symbols.playlist_add),
tooltip: 'Add to Playlist', tooltip: 'Add to Playlist',
onPressed: () { onPressed: () {
_batchAddToPlaylist( _batchAddToPlaylist(
@@ -94,7 +95,7 @@ class LibraryScreen extends HookConsumerWidget {
}, },
), ),
IconButton( IconButton(
icon: const Icon(Icons.delete), icon: const Icon(Symbols.delete),
tooltip: 'Delete', tooltip: 'Delete',
onPressed: () { onPressed: () {
_batchDelete( _batchDelete(
@@ -132,10 +133,10 @@ class LibraryScreen extends HookConsumerWidget {
), ),
); );
}, },
icon: const Icon(Icons.settings), icon: const Icon(Symbols.settings),
), ),
IconButton( IconButton(
icon: const Icon(Icons.add_circle_outline), icon: const Icon(Symbols.add_circle_outline),
tooltip: 'Import Files', tooltip: 'Import Files',
onPressed: () async { onPressed: () async {
final result = await FilePicker.platform.pickFiles( final result = await FilePicker.platform.pickFiles(
@@ -198,15 +199,15 @@ class LibraryScreen extends HookConsumerWidget {
onDestinationSelected: (index) => selectedTab.value = index, onDestinationSelected: (index) => selectedTab.value = index,
destinations: const [ destinations: const [
NavigationRailDestination( NavigationRailDestination(
icon: Icon(Icons.audiotrack), icon: Icon(Symbols.audiotrack),
label: Text('Tracks'), label: Text('Tracks'),
), ),
NavigationRailDestination( NavigationRailDestination(
icon: Icon(Icons.album), icon: Icon(Symbols.album),
label: Text('Albums'), label: Text('Albums'),
), ),
NavigationRailDestination( NavigationRailDestination(
icon: Icon(Icons.queue_music), icon: Icon(Symbols.queue_music),
label: Text('Playlists'), label: Text('Playlists'),
), ),
], ],
@@ -236,14 +237,14 @@ class LibraryScreen extends HookConsumerWidget {
appBar: isSelectionMode appBar: isSelectionMode
? AppBar( ? AppBar(
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.close), icon: const Icon(Symbols.close),
onPressed: clearSelection, onPressed: clearSelection,
), ),
title: Text('${selectedTrackIds.value.length} selected'), title: Text('${selectedTrackIds.value.length} selected'),
backgroundColor: Theme.of(context).primaryColorDark, backgroundColor: Theme.of(context).primaryColorDark,
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Icons.playlist_add), icon: const Icon(Symbols.playlist_add),
tooltip: 'Add to Playlist', tooltip: 'Add to Playlist',
onPressed: () { onPressed: () {
_batchAddToPlaylist( _batchAddToPlaylist(
@@ -255,7 +256,7 @@ class LibraryScreen extends HookConsumerWidget {
}, },
), ),
IconButton( IconButton(
icon: const Icon(Icons.delete), icon: const Icon(Symbols.delete),
tooltip: 'Delete', tooltip: 'Delete',
onPressed: () { onPressed: () {
_batchDelete( _batchDelete(
@@ -274,14 +275,14 @@ class LibraryScreen extends HookConsumerWidget {
title: const Text('Library'), title: const Text('Library'),
bottom: const TabBar( bottom: const TabBar(
tabs: [ tabs: [
Tab(text: 'Tracks', icon: Icon(Icons.audiotrack)), Tab(text: 'Tracks', icon: Icon(Symbols.audiotrack)),
Tab(text: 'Albums', icon: Icon(Icons.album)), Tab(text: 'Albums', icon: Icon(Symbols.album)),
Tab(text: 'Playlists', icon: Icon(Icons.queue_music)), Tab(text: 'Playlists', icon: Icon(Symbols.queue_music)),
], ],
), ),
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Icons.add_circle_outline), icon: const Icon(Symbols.add_circle_outline),
tooltip: 'Import Files', tooltip: 'Import Files',
onPressed: () async { onPressed: () async {
final result = await FilePicker.platform.pickFiles( final result = await FilePicker.platform.pickFiles(
@@ -480,7 +481,7 @@ class LibraryScreen extends HookConsumerWidget {
color: Colors.red, color: Colors.red,
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
child: const Icon(Icons.delete, color: Colors.white), child: const Icon(Symbols.delete, color: Colors.white),
), ),
confirmDismiss: (direction) async { confirmDismiss: (direction) async {
return await showDialog( return await showDialog(
@@ -551,7 +552,7 @@ class LibraryScreen extends HookConsumerWidget {
child: SearchBar( child: SearchBar(
onChanged: (value) => searchQuery.value = value, onChanged: (value) => searchQuery.value = value,
hintText: hintText, hintText: hintText,
leading: const Icon(Icons.search), leading: const Icon(Symbols.search),
padding: WidgetStatePropertyAll( padding: WidgetStatePropertyAll(
EdgeInsets.symmetric(horizontal: 24), EdgeInsets.symmetric(horizontal: 24),
), ),
@@ -601,7 +602,7 @@ class LibraryScreen extends HookConsumerWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
ListTile( ListTile(
leading: const Icon(Icons.playlist_add), leading: const Icon(Symbols.playlist_add),
title: const Text('Add to Playlist'), title: const Text('Add to Playlist'),
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
@@ -609,7 +610,7 @@ class LibraryScreen extends HookConsumerWidget {
}, },
), ),
ListTile( ListTile(
leading: const Icon(Icons.info_outline), leading: const Icon(Symbols.info),
title: const Text('View Details'), title: const Text('View Details'),
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
@@ -617,7 +618,7 @@ class LibraryScreen extends HookConsumerWidget {
}, },
), ),
ListTile( ListTile(
leading: const Icon(Icons.edit), leading: const Icon(Symbols.edit),
title: const Text('Edit Metadata'), title: const Text('Edit Metadata'),
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
@@ -625,7 +626,7 @@ class LibraryScreen extends HookConsumerWidget {
}, },
), ),
ListTile( ListTile(
leading: const Icon(Icons.lyrics_outlined), leading: const Icon(Symbols.lyrics),
title: const Text('Import Lyrics'), title: const Text('Import Lyrics'),
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
@@ -633,7 +634,7 @@ class LibraryScreen extends HookConsumerWidget {
}, },
), ),
ListTile( ListTile(
leading: const Icon(Icons.delete, color: Colors.red), leading: const Icon(Symbols.delete, color: Colors.red),
title: const Text( title: const Text(
'Delete Track', 'Delete Track',
style: TextStyle(color: Colors.red), style: TextStyle(color: Colors.red),

View File

@@ -20,6 +20,7 @@ import 'package:groovybox/ui/widgets/mini_player.dart';
import 'package:groovybox/ui/widgets/track_tile.dart'; import 'package:groovybox/ui/widgets/track_tile.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:media_kit/media_kit.dart'; import 'package:media_kit/media_kit.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:super_sliver_list/super_sliver_list.dart'; import 'package:super_sliver_list/super_sliver_list.dart';
@@ -136,7 +137,7 @@ class PlayerScreen extends HookConsumerWidget {
top: MediaQuery.of(context).padding.top + 16, top: MediaQuery.of(context).padding.top + 16,
left: 16, left: 16,
child: IconButton( child: IconButton(
icon: const Icon(Icons.keyboard_arrow_down), icon: const Icon(Symbols.keyboard_arrow_down),
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
iconSize: 24, iconSize: 24,
@@ -439,7 +440,7 @@ class _PlayerCoverArt extends StatelessWidget {
child: currentMetadata?.artBytes == null child: currentMetadata?.artBytes == null
? Center( ? Center(
child: Icon( child: Icon(
Icons.music_note, Symbols.music_note,
size: 80, size: 80,
color: Theme.of( color: Theme.of(
context, context,
@@ -544,7 +545,7 @@ class _PlayerLyrics extends HookConsumerWidget {
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
ElevatedButton.icon( ElevatedButton.icon(
icon: const Icon(Icons.download), icon: const Icon(Symbols.download),
label: const Text('Fetch Lyrics'), label: const Text('Fetch Lyrics'),
onPressed: () => _showFetchLyricsDialog( onPressed: () => _showFetchLyricsDialog(
context, context,
@@ -706,7 +707,7 @@ class _FetchLyricsDialog extends StatelessWidget {
children: [ children: [
ListTile( ListTile(
dense: true, dense: true,
leading: const Icon(Icons.library_music), leading: const Icon(Symbols.library_music),
title: const Text('Musixmatch'), title: const Text('Musixmatch'),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(12)), borderRadius: const BorderRadius.all(Radius.circular(12)),
@@ -725,7 +726,7 @@ class _FetchLyricsDialog extends StatelessWidget {
), ),
ListTile( ListTile(
dense: true, dense: true,
leading: const Icon(Icons.music_video), leading: const Icon(Symbols.music_video),
title: const Text('NetEase'), title: const Text('NetEase'),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(12)), borderRadius: const BorderRadius.all(Radius.circular(12)),
@@ -744,7 +745,7 @@ class _FetchLyricsDialog extends StatelessWidget {
), ),
ListTile( ListTile(
dense: true, dense: true,
leading: const Icon(Icons.file_upload), leading: const Icon(Symbols.file_upload),
title: const Text('Manual Import'), title: const Text('Manual Import'),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(12)), borderRadius: const BorderRadius.all(Radius.circular(12)),
@@ -826,7 +827,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
} }
return IconButton( return IconButton(
icon: const Icon(Icons.settings_applications), icon: const Icon(Symbols.settings_applications),
iconSize: 24, iconSize: 24,
tooltip: 'Adjust Lyrics', tooltip: 'Adjust Lyrics',
onPressed: () => _showLyricsRefreshDialog( onPressed: () => _showLyricsRefreshDialog(
@@ -901,7 +902,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
children: [ children: [
Expanded( Expanded(
child: ElevatedButton.icon( child: ElevatedButton.icon(
icon: const Icon(Icons.refresh), icon: const Icon(Symbols.refresh),
label: const Text('Re-fetch'), label: const Text('Re-fetch'),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
@@ -920,7 +921,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
), ),
Expanded( Expanded(
child: ElevatedButton.icon( child: ElevatedButton.icon(
icon: const Icon(Icons.clear), icon: const Icon(Symbols.clear),
label: const Text('Clear'), label: const Text('Clear'),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.red, backgroundColor: Colors.red,
@@ -972,7 +973,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
child: ElevatedButton.icon( child: ElevatedButton.icon(
icon: const Icon(Icons.sync), icon: const Icon(Symbols.sync),
label: const Text('Live Sync Lyrics'), label: const Text('Live Sync Lyrics'),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
@@ -989,7 +990,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
child: ElevatedButton.icon( child: ElevatedButton.icon(
icon: const Icon(Icons.tune), icon: const Icon(Symbols.tune),
label: const Text('Manual Offset'), label: const Text('Manual Offset'),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
@@ -1098,11 +1099,11 @@ class _ViewToggleButton extends StatelessWidget {
IconData getIcon() { IconData getIcon() {
switch (viewMode.value) { switch (viewMode.value) {
case ViewMode.cover: case ViewMode.cover:
return Icons.album; return Symbols.album;
case ViewMode.lyrics: case ViewMode.lyrics:
return Icons.lyrics; return Symbols.lyrics;
case ViewMode.queue: case ViewMode.queue:
return Icons.queue_music; return Symbols.queue_music;
} }
} }
@@ -1193,7 +1194,7 @@ class _QueueView extends HookConsumerWidget {
color: Colors.red, color: Colors.red,
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
child: const Icon(Icons.delete, color: Colors.white), child: const Icon(Symbols.delete, color: Colors.white),
), ),
onDismissed: (direction) => player.remove(index), onDismissed: (direction) => player.remove(index),
child: TrackTile( child: TrackTile(
@@ -1231,7 +1232,7 @@ class _QueueView extends HookConsumerWidget {
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
child: const Icon( child: const Icon(
Icons.delete, Symbols.delete,
color: Colors.white, color: Colors.white,
), ),
), ),
@@ -1636,7 +1637,7 @@ class _PlayerControls extends HookWidget {
builder: (context, snapshot) { builder: (context, snapshot) {
final shuffle = snapshot.data ?? false; final shuffle = snapshot.data ?? false;
return Icon( return Icon(
Icons.shuffle, Symbols.shuffle,
color: shuffle color: shuffle
? Theme.of(context).colorScheme.primary ? Theme.of(context).colorScheme.primary
: Theme.of(context).disabledColor, : Theme.of(context).disabledColor,
@@ -1649,7 +1650,7 @@ class _PlayerControls extends HookWidget {
), ),
// Previous // Previous
IconButton( IconButton(
icon: const Icon(Icons.skip_previous, size: 32), icon: const Icon(Symbols.skip_previous, size: 32),
onPressed: player.previous, onPressed: player.previous,
), ),
// Play/Pause // Play/Pause
@@ -1669,7 +1670,7 @@ class _PlayerControls extends HookWidget {
); );
}, },
child: Icon( child: Icon(
playing ? Icons.pause : Icons.play_arrow, playing ? Symbols.pause : Symbols.play_arrow,
key: ValueKey<bool>(playing), key: ValueKey<bool>(playing),
size: 48, size: 48,
), ),
@@ -1681,7 +1682,7 @@ class _PlayerControls extends HookWidget {
), ),
// Next // Next
IconButton( IconButton(
icon: const Icon(Icons.skip_next, size: 32), icon: const Icon(Symbols.skip_next, size: 32),
onPressed: player.next, onPressed: player.next,
), ),
// Loop Mode // Loop Mode
@@ -1695,15 +1696,15 @@ class _PlayerControls extends HookWidget {
Color? color; Color? color;
switch (mode) { switch (mode) {
case PlaylistMode.none: case PlaylistMode.none:
icon = Icons.repeat; icon = Symbols.repeat;
color = Theme.of(context).disabledColor; color = Theme.of(context).disabledColor;
break; break;
case PlaylistMode.single: case PlaylistMode.single:
icon = Icons.repeat_one; icon = Symbols.repeat_one;
color = Theme.of(context).colorScheme.primary; color = Theme.of(context).colorScheme.primary;
break; break;
case PlaylistMode.loop: case PlaylistMode.loop:
icon = Icons.repeat; icon = Symbols.repeat;
color = Theme.of(context).colorScheme.primary; color = Theme.of(context).colorScheme.primary;
break; break;
} }
@@ -1744,10 +1745,10 @@ class _PlayerControls extends HookWidget {
children: [ children: [
Icon( Icon(
volume == 0 volume == 0
? Icons.volume_off ? Symbols.volume_off
: volume < 50 : volume < 50
? Icons.volume_down ? Symbols.volume_down
: Icons.volume_up, : Symbols.volume_up,
color: Theme.of(context).colorScheme.onSurfaceVariant, color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
Expanded( Expanded(
@@ -1812,12 +1813,12 @@ class _LiveLyricsSyncDialog extends HookConsumerWidget {
appBar: AppBar( appBar: AppBar(
title: const Text('Live Lyrics Sync'), title: const Text('Live Lyrics Sync'),
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.close), icon: const Icon(Symbols.close),
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
), ),
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Icons.check), icon: const Icon(Symbols.check),
onPressed: () async { onPressed: () async {
// Store context before async operation // Store context before async operation
final navigator = Navigator.of(context); final navigator = Navigator.of(context);
@@ -1890,30 +1891,30 @@ class _LiveLyricsSyncDialog extends HookConsumerWidget {
runAlignment: WrapAlignment.center, runAlignment: WrapAlignment.center,
children: [ children: [
ElevatedButton.icon( ElevatedButton.icon(
icon: const Icon(Icons.fast_rewind), icon: const Icon(Symbols.fast_rewind),
label: const Text('-100ms'), label: const Text('-100ms'),
onPressed: () => onPressed: () =>
tempOffset.value = (tempOffset.value - 100), tempOffset.value = (tempOffset.value - 100),
), ),
ElevatedButton.icon( ElevatedButton.icon(
icon: const Icon(Icons.skip_previous), icon: const Icon(Symbols.skip_previous),
label: const Text('-10ms'), label: const Text('-10ms'),
onPressed: () => onPressed: () =>
tempOffset.value = (tempOffset.value - 10), tempOffset.value = (tempOffset.value - 10),
), ),
ElevatedButton.icon( ElevatedButton.icon(
icon: const Icon(Icons.refresh), icon: const Icon(Symbols.refresh),
label: const Text('Reset'), label: const Text('Reset'),
onPressed: () => tempOffset.value = 0, onPressed: () => tempOffset.value = 0,
), ),
ElevatedButton.icon( ElevatedButton.icon(
icon: const Icon(Icons.skip_next), icon: const Icon(Symbols.skip_next),
label: const Text('+10ms'), label: const Text('+10ms'),
onPressed: () => onPressed: () =>
tempOffset.value = (tempOffset.value + 10), tempOffset.value = (tempOffset.value + 10),
), ),
ElevatedButton.icon( ElevatedButton.icon(
icon: const Icon(Icons.fast_forward), icon: const Icon(Symbols.fast_forward),
label: const Text('+100ms'), label: const Text('+100ms'),
onPressed: () => onPressed: () =>
tempOffset.value = (tempOffset.value + 100), tempOffset.value = (tempOffset.value + 100),
@@ -1947,7 +1948,7 @@ class _LiveLyricsSyncDialog extends HookConsumerWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
IconButton( IconButton(
icon: const Icon(Icons.skip_previous, size: 32), icon: const Icon(Symbols.skip_previous, size: 32),
onPressed: player.previous, onPressed: player.previous,
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
@@ -1958,7 +1959,7 @@ class _LiveLyricsSyncDialog extends HookConsumerWidget {
final playing = snapshot.data ?? false; final playing = snapshot.data ?? false;
return IconButton.filled( return IconButton.filled(
icon: Icon( icon: Icon(
playing ? Icons.pause : Icons.play_arrow, playing ? Symbols.pause : Symbols.play_arrow,
size: 48, size: 48,
), ),
onPressed: playing ? player.pause : player.play, onPressed: playing ? player.pause : player.play,
@@ -1968,7 +1969,7 @@ class _LiveLyricsSyncDialog extends HookConsumerWidget {
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
IconButton( IconButton(
icon: const Icon(Icons.skip_next, size: 32), icon: const Icon(Symbols.skip_next, size: 32),
onPressed: player.next, onPressed: player.next,
), ),
], ],

View File

@@ -4,6 +4,7 @@ import 'package:groovybox/data/playlist_repository.dart';
import 'package:groovybox/providers/audio_provider.dart'; import 'package:groovybox/providers/audio_provider.dart';
import 'package:groovybox/ui/widgets/track_tile.dart'; import 'package:groovybox/ui/widgets/track_tile.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:material_symbols_icons/symbols.dart';
class PlaylistDetailScreen extends HookConsumerWidget { class PlaylistDetailScreen extends HookConsumerWidget {
final Playlist playlist; final Playlist playlist;
@@ -36,7 +37,7 @@ class PlaylistDetailScreen extends HookConsumerWidget {
), ),
child: const Center( child: const Center(
child: Icon( child: Icon(
Icons.queue_music, Symbols.queue_music,
size: 80, size: 80,
color: Colors.white70, color: Colors.white70,
), ),
@@ -73,7 +74,7 @@ class PlaylistDetailScreen extends HookConsumerWidget {
onPressed: () { onPressed: () {
_playPlaylist(ref, tracks); _playPlaylist(ref, tracks);
}, },
icon: const Icon(Icons.play_arrow), icon: const Icon(Symbols.play_arrow),
label: const Text('Play All'), label: const Text('Play All'),
), ),
), ),

View File

@@ -5,6 +5,7 @@ import 'package:groovybox/providers/watch_folder_provider.dart';
import 'package:groovybox/providers/remote_provider.dart'; import 'package:groovybox/providers/remote_provider.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
class SettingsScreen extends ConsumerWidget { class SettingsScreen extends ConsumerWidget {
@@ -99,7 +100,7 @@ class SettingsScreen extends ConsumerWidget {
IconButton( IconButton(
onPressed: () => onPressed: () =>
_scanLibraries(context, ref), _scanLibraries(context, ref),
icon: const Icon(Icons.refresh), icon: const Icon(Symbols.refresh),
tooltip: 'Scan Libraries', tooltip: 'Scan Libraries',
visualDensity: const VisualDensity( visualDensity: const VisualDensity(
horizontal: -4, horizontal: -4,
@@ -109,7 +110,7 @@ class SettingsScreen extends ConsumerWidget {
IconButton( IconButton(
onPressed: () => onPressed: () =>
_addMusicLibrary(context, ref), _addMusicLibrary(context, ref),
icon: const Icon(Icons.add), icon: const Icon(Symbols.add),
tooltip: 'Add Music Library', tooltip: 'Add Music Library',
visualDensity: const VisualDensity( visualDensity: const VisualDensity(
horizontal: -4, horizontal: -4,
@@ -165,7 +166,9 @@ class SettingsScreen extends ConsumerWidget {
}, },
), ),
IconButton( IconButton(
icon: const Icon(Icons.delete), icon: const Icon(
Symbols.delete,
),
onPressed: () { onPressed: () {
ref ref
.read( .read(
@@ -215,7 +218,7 @@ class SettingsScreen extends ConsumerWidget {
IconButton( IconButton(
onPressed: () => onPressed: () =>
_indexRemoteProviders(context, ref), _indexRemoteProviders(context, ref),
icon: const Icon(Icons.refresh), icon: const Icon(Symbols.refresh),
tooltip: 'Index Remote Providers', tooltip: 'Index Remote Providers',
visualDensity: const VisualDensity( visualDensity: const VisualDensity(
horizontal: -4, horizontal: -4,
@@ -225,7 +228,7 @@ class SettingsScreen extends ConsumerWidget {
IconButton( IconButton(
onPressed: () => onPressed: () =>
_addRemoteProvider(context, ref), _addRemoteProvider(context, ref),
icon: const Icon(Icons.add), icon: const Icon(Symbols.add),
tooltip: 'Add Remote Provider', tooltip: 'Add Remote Provider',
visualDensity: const VisualDensity( visualDensity: const VisualDensity(
horizontal: -4, horizontal: -4,
@@ -281,7 +284,9 @@ class SettingsScreen extends ConsumerWidget {
}, },
), ),
IconButton( IconButton(
icon: const Icon(Icons.delete), icon: const Icon(
Symbols.delete,
),
onPressed: () { onPressed: () {
ref ref
.read( .read(

View File

@@ -4,6 +4,7 @@ import 'package:groovybox/data/playlist_repository.dart';
import 'package:groovybox/ui/screens/album_detail_screen.dart'; import 'package:groovybox/ui/screens/album_detail_screen.dart';
import 'package:groovybox/ui/widgets/universal_image.dart'; import 'package:groovybox/ui/widgets/universal_image.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:material_symbols_icons/symbols.dart';
class AlbumsTab extends HookConsumerWidget { class AlbumsTab extends HookConsumerWidget {
const AlbumsTab({super.key}); const AlbumsTab({super.key});
@@ -52,7 +53,7 @@ class AlbumsTab extends HookConsumerWidget {
child: UniversalImage( child: UniversalImage(
uri: album.artUri, uri: album.artUri,
fit: BoxFit.cover, fit: BoxFit.cover,
fallbackIcon: Icons.album, fallbackIcon: Symbols.album,
fallbackIconSize: 48, fallbackIconSize: 48,
), ),
), ),

View File

@@ -3,6 +3,7 @@ import 'package:groovybox/data/db.dart';
import 'package:groovybox/data/playlist_repository.dart'; import 'package:groovybox/data/playlist_repository.dart';
import 'package:groovybox/ui/screens/playlist_detail_screen.dart'; import 'package:groovybox/ui/screens/playlist_detail_screen.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
class PlaylistsTab extends HookConsumerWidget { class PlaylistsTab extends HookConsumerWidget {
@@ -16,8 +17,8 @@ class PlaylistsTab extends HookConsumerWidget {
body: Column( body: Column(
children: [ children: [
ListTile( ListTile(
leading: const Icon(Icons.add), leading: const Icon(Symbols.add),
trailing: const Icon(Icons.chevron_right).padding(right: 8), trailing: const Icon(Symbols.chevron_right).padding(right: 8),
title: Text('Create One'), title: Text('Create One'),
subtitle: Text('Add a new playlist'), subtitle: Text('Add a new playlist'),
onTap: () async { onTap: () async {
@@ -70,13 +71,13 @@ class PlaylistsTab extends HookConsumerWidget {
itemBuilder: (context, index) { itemBuilder: (context, index) {
final playlist = playlists[index]; final playlist = playlists[index];
return ListTile( return ListTile(
leading: const Icon(Icons.queue_music), leading: const Icon(Symbols.queue_music),
title: Text(playlist.name), title: Text(playlist.name),
subtitle: Text( subtitle: Text(
'${playlist.createdAt.day}/${playlist.createdAt.month}/${playlist.createdAt.year}', '${playlist.createdAt.day}/${playlist.createdAt.month}/${playlist.createdAt.year}',
), ),
trailing: IconButton( trailing: IconButton(
icon: const Icon(Icons.delete), icon: const Icon(Symbols.delete),
onPressed: () => repo.deletePlaylist(playlist.id), onPressed: () => repo.deletePlaylist(playlist.id),
), ),
onTap: () { onTap: () {

View File

@@ -7,6 +7,7 @@ import 'package:groovybox/ui/screens/player_screen.dart';
import 'package:groovybox/ui/widgets/track_tile.dart'; import 'package:groovybox/ui/widgets/track_tile.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:media_kit/media_kit.dart'; import 'package:media_kit/media_kit.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -151,7 +152,7 @@ class _MobileMiniPlayer extends HookConsumerWidget {
: Container( : Container(
color: Colors.grey[800], color: Colors.grey[800],
child: const Icon( child: const Icon(
Icons.music_note, Symbols.music_note,
color: Colors.white54, color: Colors.white54,
), ),
), ),
@@ -205,7 +206,7 @@ class _MobileMiniPlayer extends HookConsumerWidget {
); );
}, },
child: Icon( child: Icon(
playing ? Icons.pause : Icons.play_arrow, playing ? Symbols.pause : Symbols.play_arrow,
key: ValueKey<bool>(playing), key: ValueKey<bool>(playing),
), ),
), ),
@@ -216,7 +217,7 @@ class _MobileMiniPlayer extends HookConsumerWidget {
), ),
// Next Button // Next Button
IconButton( IconButton(
icon: const Icon(Icons.skip_next), icon: const Icon(Symbols.skip_next),
onPressed: player.next, onPressed: player.next,
iconSize: 24, iconSize: 24,
), ),
@@ -376,7 +377,7 @@ class _DesktopMiniPlayer extends HookConsumerWidget {
: Container( : Container(
color: Colors.grey[800], color: Colors.grey[800],
child: const Icon( child: const Icon(
Icons.music_note, Symbols.music_note,
color: Colors.white54, color: Colors.white54,
), ),
), ),
@@ -434,15 +435,15 @@ class _DesktopMiniPlayer extends HookConsumerWidget {
Color? color; Color? color;
switch (mode) { switch (mode) {
case PlaylistMode.none: case PlaylistMode.none:
icon = Icons.repeat; icon = Symbols.repeat;
color = Theme.of(context).disabledColor; color = Theme.of(context).disabledColor;
break; break;
case PlaylistMode.single: case PlaylistMode.single:
icon = Icons.repeat_one; icon = Symbols.repeat_one;
color = Theme.of(context).colorScheme.primary; color = Theme.of(context).colorScheme.primary;
break; break;
case PlaylistMode.loop: case PlaylistMode.loop:
icon = Icons.repeat; icon = Symbols.repeat;
color = Theme.of(context).colorScheme.primary; color = Theme.of(context).colorScheme.primary;
break; break;
} }
@@ -469,7 +470,7 @@ class _DesktopMiniPlayer extends HookConsumerWidget {
}, },
), ),
IconButton( IconButton(
icon: const Icon(Icons.skip_previous), icon: const Icon(Symbols.skip_previous),
onPressed: player.previous, onPressed: player.previous,
iconSize: 24, iconSize: 24,
), ),
@@ -499,7 +500,9 @@ class _DesktopMiniPlayer extends HookConsumerWidget {
); );
}, },
child: Icon( child: Icon(
playing ? Icons.pause : Icons.play_arrow, playing
? Symbols.pause
: Symbols.play_arrow,
key: ValueKey<bool>(playing), key: ValueKey<bool>(playing),
), ),
), ),
@@ -511,12 +514,12 @@ class _DesktopMiniPlayer extends HookConsumerWidget {
}, },
), ),
IconButton( IconButton(
icon: const Icon(Icons.skip_next), icon: const Icon(Symbols.skip_next),
onPressed: player.next, onPressed: player.next,
iconSize: 24, iconSize: 24,
), ),
IconButton( IconButton(
icon: const Icon(Icons.queue_music), icon: const Icon(Symbols.queue_music),
onPressed: () => onPressed: () =>
_showQueueDialog(context, ref, player), _showQueueDialog(context, ref, player),
iconSize: 24, iconSize: 24,
@@ -530,7 +533,7 @@ class _DesktopMiniPlayer extends HookConsumerWidget {
child: Row( child: Row(
children: [ children: [
Icon( Icon(
Icons.volume_up, Symbols.volume_up,
size: 16, size: 16,
color: Theme.of( color: Theme.of(
context, context,
@@ -599,7 +602,7 @@ class _DesktopMiniPlayer extends HookConsumerWidget {
const Text('Queue', style: TextStyle(fontSize: 20)), const Text('Queue', style: TextStyle(fontSize: 20)),
const Spacer(), const Spacer(),
IconButton( IconButton(
icon: const Icon(Icons.close), icon: const Icon(Symbols.close),
onPressed: () => Navigator.of(bottomSheetContext).pop(), onPressed: () => Navigator.of(bottomSheetContext).pop(),
), ),
], ],
@@ -650,7 +653,7 @@ class _DesktopMiniPlayer extends HookConsumerWidget {
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
child: const Icon( child: const Icon(
Icons.delete, Symbols.delete,
color: Colors.white, color: Colors.white,
), ),
), ),
@@ -696,7 +699,7 @@ class _DesktopMiniPlayer extends HookConsumerWidget {
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20), padding: const EdgeInsets.only(right: 20),
child: const Icon( child: const Icon(
Icons.delete, Symbols.delete,
color: Colors.white, color: Colors.white,
), ),
), ),

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:groovybox/data/db.dart' as db; import 'package:groovybox/data/db.dart' as db;
import 'package:groovybox/ui/widgets/universal_image.dart'; import 'package:groovybox/ui/widgets/universal_image.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
class TrackTile extends StatelessWidget { class TrackTile extends StatelessWidget {
@@ -52,7 +53,7 @@ class TrackTile extends StatelessWidget {
uri: track.artUri, uri: track.artUri,
fit: BoxFit.cover, fit: BoxFit.cover,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
fallbackIcon: Icons.music_note, fallbackIcon: Symbols.music_note,
fallbackIconSize: 24, fallbackIconSize: 24,
).clipRRect(all: 8), ).clipRRect(all: 8),
), ),
@@ -79,12 +80,12 @@ class TrackTile extends StatelessWidget {
), ),
trailing: showTrailingIcon trailing: showTrailingIcon
? IconButton( ? IconButton(
icon: const Icon(Icons.more_vert), icon: const Icon(Symbols.more_vert),
onPressed: onTrailingPressed, onPressed: onTrailingPressed,
) )
: isPlaying : isPlaying
? Icon( ? Icon(
Icons.play_arrow, Symbols.play_arrow,
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
) )
: null, : null,

View File

@@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
class UniversalImage extends StatelessWidget { class UniversalImage extends StatelessWidget {
final String? uri; final String? uri;
@@ -21,7 +22,7 @@ class UniversalImage extends StatelessWidget {
this.width, this.width,
this.height, this.height,
this.fallback, this.fallback,
this.fallbackIcon = Icons.image, this.fallbackIcon = Symbols.image,
this.fallbackIconSize = 48, this.fallbackIconSize = 48,
this.fallbackIconColor = Colors.white54, this.fallbackIconColor = Colors.white54,
this.borderRadius, this.borderRadius,

View File

@@ -26,6 +26,8 @@
<string>$(PRODUCT_COPYRIGHT)</string> <string>$(PRODUCT_COPYRIGHT)</string>
<key>NSMainNibFile</key> <key>NSMainNibFile</key>
<string>MainMenu</string> <string>MainMenu</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.music</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string>NSApplication</string> <string>NSApplication</string>
</dict> </dict>

View File

@@ -185,6 +185,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.1" version: "1.3.1"
chalkdart:
dependency: transitive
description:
name: chalkdart
sha256: "7ffc6bd39c81453fb9ba8dbce042a9c960219b75ea1c07196a7fa41c2fab9e86"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@@ -688,6 +696,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.11.1" version: "0.11.1"
material_symbols_icons:
dependency: "direct main"
description:
name: material_symbols_icons
sha256: "02555a48e1ec02b16e532dfd4ef13c4f6bf7ec7c20230e58e56641a393433dc3"
url: "https://pub.dev"
source: hosted
version: "4.2892.0"
media_kit: media_kit:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@@ -44,21 +44,22 @@ dependencies:
path_provider: ^2.1.5 path_provider: ^2.1.5
path: ^1.9.1 path: ^1.9.1
riverpod_annotation: ^3.0.3 riverpod_annotation: ^3.0.3
file_picker: ^10.3.7 file_picker: ^10.3.8
flutter_media_metadata: ^1.0.0+1 flutter_media_metadata: ^1.0.0+1
animations: ^2.1.1 animations: ^2.1.1
gap: ^3.0.1 gap: ^3.0.1
styled_widget: ^0.4.1 styled_widget: ^0.4.1
super_sliver_list: ^0.4.1 super_sliver_list: ^0.4.1
dio: ^5.0.0 dio: ^5.9.0
http: ^1.2.2 http: ^1.6.0
audio_service: ^0.18.18 audio_service: ^0.18.18
palette_generator: ^0.3.3+4 palette_generator: ^0.3.3+7
watcher: ^1.2.0 watcher: ^1.2.0
shared_preferences: ^2.3.5 shared_preferences: ^2.5.4
jellyfin_dart: ^0.1.2 jellyfin_dart: ^0.1.2
cached_network_image: ^3.4.1 cached_network_image: ^3.4.1
flutter_cache_manager: ^3.4.1 flutter_cache_manager: ^3.4.1
material_symbols_icons: ^4.2892.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@@ -1,30 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:groovybox/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const GroovyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}