refactor(ui): Lucide migration unit 2 — Icons.* -> LucideIcons.* sweep (#60)

Mechanical sweep across 30 files: every Material Icons.* replaced with
the signed-off Lucide equivalent + a flutter_lucide import per file.
Zero Material Icons.* remain in lib/; no unused imports.

Judgment-call mappings: album->disc_3, library_music->library_big,
playlist_play->list_video, graphic_eq->audio_lines,
system_update->download, restore->archive_restore,
download_done->circle_check_big.

track_actions_sheet like menu row: collapsed `liked ? favorite :
favorite_border` to a single LucideIcons.heart (the row's Like/Unlike
text label conveys state). Icon-only LikeButton + the notification keep
the filled-vs-outline shape per the design decision.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-18 20:43:05 -04:00
parent 84f16c25f6
commit 835592f073
30 changed files with 113 additions and 83 deletions
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -34,7 +35,7 @@ class AdminLandingScreen extends ConsumerWidget {
children: [
AdminSectionCard(
key: const Key('admin_card_requests'),
icon: Icons.inbox,
icon: LucideIcons.inbox,
title: 'Requests',
subtitle: 'Approve or reject Lidarr requests',
count: c.requests,
@@ -42,7 +43,7 @@ class AdminLandingScreen extends ConsumerWidget {
),
AdminSectionCard(
key: const Key('admin_card_quarantine'),
icon: Icons.warning_amber,
icon: LucideIcons.triangle_alert,
title: 'Quarantine',
subtitle: 'Resolve scan failures',
count: c.quarantine,
@@ -50,7 +51,7 @@ class AdminLandingScreen extends ConsumerWidget {
),
AdminSectionCard(
key: const Key('admin_card_users'),
icon: Icons.people,
icon: LucideIcons.users,
title: 'Users',
subtitle: 'Manage accounts and invites',
count: c.users,
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -59,7 +60,7 @@ class AdminUsersScreen extends ConsumerWidget {
trailing: TextButton.icon(
key: const Key('invite_generate_button'),
onPressed: () => _showGenerateInvite(context, ref),
icon: Icon(Icons.add, color: fs.parchment),
icon: Icon(LucideIcons.plus, color: fs.parchment),
label: Text('Generate',
style: TextStyle(color: fs.parchment)),
),
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import '../../models/admin_quarantine_item.dart';
import '../../theme/theme_extension.dart';
@@ -45,7 +46,7 @@ class AdminQuarantineRow extends StatelessWidget {
),
trailing: PopupMenuButton<String>(
key: Key('admin_quarantine_menu_${item.trackId}'),
icon: Icon(Icons.more_vert, color: fs.parchment),
icon: Icon(LucideIcons.ellipsis_vertical, color: fs.parchment),
onSelected: (action) async {
switch (action) {
case 'resolve':
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import '../../models/admin_user.dart';
import '../../theme/theme_extension.dart';
@@ -29,7 +30,7 @@ class AdminUserRow extends StatelessWidget {
_Badge(label: 'auto-approve', color: fs.moss),
],
),
trailing: Icon(Icons.chevron_right, color: fs.ash),
trailing: Icon(LucideIcons.chevron_right, color: fs.ash),
onTap: onTap,
);
}
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter/services.dart';
import '../../models/invite.dart';
@@ -31,7 +32,7 @@ class InviteRow extends StatelessWidget {
),
),
IconButton(
icon: Icon(Icons.copy, color: fs.ash, size: 18),
icon: Icon(LucideIcons.copy, color: fs.ash, size: 18),
tooltip: 'Copy',
onPressed: () =>
Clipboard.setData(ClipboardData(text: invite.token)),
@@ -63,7 +64,7 @@ class InviteRow extends StatelessWidget {
],
),
trailing: IconButton(
icon: Icon(Icons.delete_outline, color: fs.oxblood),
icon: Icon(LucideIcons.trash_2, color: fs.oxblood),
tooltip: 'Revoke',
onPressed: onRevoke,
),
@@ -1,6 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -167,7 +168,7 @@ class _DiscoverScreenState extends ConsumerState<DiscoverScreen> {
backgroundColor: fs.obsidian,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: fs.parchment),
icon: Icon(LucideIcons.arrow_left, color: fs.parchment),
onPressed: () => context.pop(),
),
title: Text('Discover', style: TextStyle(color: fs.parchment)),
@@ -193,7 +194,7 @@ class _DiscoverScreenState extends ConsumerState<DiscoverScreen> {
borderSide: BorderSide(color: fs.accent),
),
suffixIcon: IconButton(
icon: Icon(Icons.search, color: fs.ash),
icon: Icon(LucideIcons.search, color: fs.ash),
onPressed: _runSearch,
),
),
@@ -336,14 +337,14 @@ class _ResultTile extends StatelessWidget {
height: 56,
color: fs.slate,
child: row.imageUrl.isEmpty
? Icon(Icons.album, color: fs.ash)
? Icon(LucideIcons.disc_3, color: fs.ash)
: CachedNetworkImage(
imageUrl: row.imageUrl,
fit: BoxFit.cover,
fadeInDuration: const Duration(milliseconds: 120),
fadeOutDuration: Duration.zero,
errorWidget: (_, __, ___) =>
Icon(Icons.album, color: fs.ash),
Icon(LucideIcons.disc_3, color: fs.ash),
),
),
),
@@ -419,14 +420,14 @@ class _SuggestionTile extends StatelessWidget {
height: 56,
color: fs.slate,
child: s.imageUrl.isEmpty
? Icon(Icons.person, color: fs.ash)
? Icon(LucideIcons.user, color: fs.ash)
: CachedNetworkImage(
imageUrl: s.imageUrl,
fit: BoxFit.cover,
fadeInDuration: const Duration(milliseconds: 120),
fadeOutDuration: Duration.zero,
errorWidget: (_, __, ___) =>
Icon(Icons.person, color: fs.ash),
Icon(LucideIcons.user, color: fs.ash),
),
),
),
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../api/endpoints/likes.dart';
@@ -115,7 +116,7 @@ class AlbumDetailScreen extends ConsumerWidget {
width: 48, height: 48,
decoration: BoxDecoration(color: fs.accent, shape: BoxShape.circle),
child: IconButton(
icon: Icon(Icons.play_arrow, color: fs.parchment),
icon: Icon(LucideIcons.play, color: fs.parchment),
onPressed: () => ref.read(playerActionsProvider).playTracks(r.tracks),
),
),
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -90,7 +91,7 @@ class ArtistDetailScreen extends ConsumerWidget {
width: 48, height: 48,
decoration: BoxDecoration(color: fs.accent, shape: BoxShape.circle),
child: IconButton(
icon: Icon(Icons.play_arrow, color: fs.parchment),
icon: Icon(LucideIcons.play, color: fs.parchment),
onPressed: () async {
try {
final tracks =
+3 -2
View File
@@ -1,5 +1,6 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -245,12 +246,12 @@ class _PlaylistsSection extends StatelessWidget {
if (offline) ...const [
_OfflinePoolCard(
label: 'Recently played',
icon: Icons.history,
icon: LucideIcons.history,
kind: _OfflinePoolKind.recentlyPlayed,
),
_OfflinePoolCard(
label: 'Liked',
icon: Icons.favorite,
icon: LucideIcons.heart,
kind: _OfflinePoolKind.liked,
),
],
@@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:drift/drift.dart' as drift;
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -382,7 +383,7 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen>
IconButton(
key: const Key('shuffle_all_button'),
tooltip: 'Shuffle all',
icon: Icon(Icons.shuffle, color: fs.parchment),
icon: Icon(LucideIcons.shuffle, color: fs.parchment),
onPressed: () async {
final messenger = ScaffoldMessenger.of(context);
final refs = await ref.read(shuffleSourceProvider).tracks();
@@ -866,7 +867,7 @@ class _QuarantineTile extends ConsumerWidget {
),
IconButton(
tooltip: 'Unhide',
icon: Icon(Icons.restore, color: fs.ash, size: 20),
icon: Icon(LucideIcons.archive_restore, color: fs.ash, size: 20),
onPressed: () async {
try {
await ref.read(myQuarantineProvider.notifier).unflag(row.trackId);
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../cache/audio_cache_manager.dart';
@@ -21,7 +22,7 @@ class CachedIndicator extends ConsumerWidget {
if (snap.data != true) return const SizedBox.shrink();
return Padding(
padding: const EdgeInsets.only(left: 4),
child: Icon(Icons.download_done, size: 14, color: fs.accent),
child: Icon(LucideIcons.circle_check_big, size: 14, color: fs.accent),
);
},
);
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import '../../theme/theme_extension.dart';
@@ -85,7 +86,7 @@ class _PlayCircleButtonState extends State<PlayCircleButton> {
),
)
: Icon(
Icons.play_arrow,
LucideIcons.play,
color: fs.parchment,
size: iconSize,
),
@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -376,13 +377,13 @@ class _TopBar extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Row(children: [
IconButton(
icon: Icon(Icons.expand_more, color: fs.parchment),
icon: Icon(LucideIcons.chevron_down, color: fs.parchment),
tooltip: 'Close',
onPressed: () => Navigator.of(context).maybePop(),
),
const Spacer(),
IconButton(
icon: Icon(Icons.queue_music, color: fs.parchment),
icon: Icon(LucideIcons.list_music, color: fs.parchment),
tooltip: 'Queue',
onPressed: () => GoRouter.of(context).push('/queue'),
),
@@ -535,13 +536,13 @@ class _PrimaryControls extends StatelessWidget {
children: [
IconButton(
iconSize: 36,
icon: Icon(Icons.skip_previous, color: fs.parchment),
icon: Icon(LucideIcons.skip_back, color: fs.parchment),
onPressed: () => ref.read(audioHandlerProvider).skipToPrevious(),
),
IconButton(
iconSize: 72,
icon: Icon(
isPlaying ? Icons.pause_circle_filled : Icons.play_circle_filled,
isPlaying ? LucideIcons.circle_pause : LucideIcons.circle_play,
color: fs.accent,
),
onPressed: () {
@@ -555,7 +556,7 @@ class _PrimaryControls extends StatelessWidget {
),
IconButton(
iconSize: 36,
icon: Icon(Icons.skip_next, color: fs.parchment),
icon: Icon(LucideIcons.skip_forward, color: fs.parchment),
onPressed: () => ref.read(audioHandlerProvider).skipToNext(),
),
],
@@ -589,7 +590,7 @@ class _SecondaryControls extends StatelessWidget {
IconButton(
tooltip: shuffleOn ? 'Shuffle on' : 'Shuffle off',
icon: Icon(
Icons.shuffle,
LucideIcons.shuffle,
color: shuffleOn ? fs.accent : fs.ash,
),
onPressed: actions.toggleShuffle,
@@ -602,8 +603,8 @@ class _SecondaryControls extends StatelessWidget {
},
icon: Icon(
repeatMode == AudioServiceRepeatMode.one
? Icons.repeat_one
: Icons.repeat,
? LucideIcons.repeat_1
: LucideIcons.repeat,
color: repeatMode == AudioServiceRepeatMode.none
? fs.ash
: fs.accent,
@@ -612,7 +613,7 @@ class _SecondaryControls extends StatelessWidget {
),
IconButton(
tooltip: 'Queue',
icon: Icon(Icons.queue_music, color: fs.ash),
icon: Icon(LucideIcons.list_music, color: fs.ash),
onPressed: () => GoRouter.of(context).push('/queue'),
),
LikeButton(kind: LikeKind.track, id: media.id, size: 22),
+4 -3
View File
@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:audio_service/audio_service.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -229,7 +230,7 @@ class _PlayControls extends StatelessWidget {
child: IconButton(
padding: EdgeInsets.zero,
iconSize: 22,
icon: Icon(Icons.skip_previous, color: fs.parchment),
icon: Icon(LucideIcons.skip_back, color: fs.parchment),
onPressed: () => ref.read(audioHandlerProvider).skipToPrevious(),
),
),
@@ -240,7 +241,7 @@ class _PlayControls extends StatelessWidget {
padding: EdgeInsets.zero,
iconSize: 32,
icon: Icon(
isPlaying ? Icons.pause_circle_filled : Icons.play_circle_filled,
isPlaying ? LucideIcons.circle_pause : LucideIcons.circle_play,
color: fs.accent,
),
onPressed: () {
@@ -259,7 +260,7 @@ class _PlayControls extends StatelessWidget {
child: IconButton(
padding: EdgeInsets.zero,
iconSize: 22,
icon: Icon(Icons.skip_next, color: fs.parchment),
icon: Icon(LucideIcons.skip_forward, color: fs.parchment),
onPressed: () => ref.read(audioHandlerProvider).skipToNext(),
),
),
+3 -2
View File
@@ -1,5 +1,6 @@
import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -20,7 +21,7 @@ class QueueScreen extends ConsumerWidget {
backgroundColor: fs.obsidian,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: fs.parchment),
icon: Icon(LucideIcons.arrow_left, color: fs.parchment),
onPressed: () => context.pop(),
),
title: Text('Queue', style: TextStyle(color: fs.parchment)),
@@ -84,7 +85,7 @@ class _QueueRow extends StatelessWidget {
if (isCurrent)
Padding(
padding: const EdgeInsets.only(right: 8),
child: Icon(Icons.graphic_eq, color: fs.accent, size: 16),
child: Icon(LucideIcons.audio_lines, color: fs.accent, size: 16),
),
Expanded(
child: Column(
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -54,7 +55,7 @@ class PlaylistDetailScreen extends ConsumerWidget {
backgroundColor: fs.obsidian,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: fs.parchment),
icon: Icon(LucideIcons.arrow_left, color: fs.parchment),
onPressed: () => context.pop(),
),
title: headerName.isEmpty
@@ -268,7 +269,7 @@ class _Header extends ConsumerWidget {
OutlinedButton.icon(
key: const Key('regenerate_playlist_button'),
onPressed: () => _regenerate(context, ref),
icon: const Icon(Icons.refresh, size: 16),
icon: const Icon(LucideIcons.refresh_cw, size: 16),
label: const Text('Regenerate'),
),
const SizedBox(width: 8),
@@ -281,7 +282,7 @@ class _Header extends ConsumerWidget {
source: p.refreshable ? p.systemVariant : null,
);
},
icon: const Icon(Icons.play_arrow),
icon: const Icon(LucideIcons.play),
label: const Text('Play'),
style: FilledButton.styleFrom(
backgroundColor: fs.accent,
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -82,11 +83,11 @@ class _PlaylistTile extends StatelessWidget {
height: 56,
color: fs.slate,
child: playlist.coverUrl.isEmpty
? Icon(Icons.queue_music, color: fs.ash)
? Icon(LucideIcons.list_music, color: fs.ash)
: ServerImage(
url: playlist.coverUrl,
fit: BoxFit.cover,
fallback: Icon(Icons.queue_music, color: fs.ash),
fallback: Icon(LucideIcons.list_music, color: fs.ash),
),
),
),
@@ -128,7 +129,7 @@ class _PlaylistTile extends StatelessWidget {
],
),
),
Icon(Icons.chevron_right, color: fs.ash),
Icon(LucideIcons.chevron_right, color: fs.ash),
]),
),
);
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -55,12 +56,12 @@ class PlaylistCard extends ConsumerWidget {
// errorBuilder shows the queue_music icon over the
// slate background.
child: playlist.coverUrl.isEmpty
? Icon(Icons.queue_music, color: fs.ash, size: 56)
? Icon(LucideIcons.list_music, color: fs.ash, size: 56)
: ServerImage(
url: playlist.coverUrl,
fit: BoxFit.cover,
fallback:
Icon(Icons.queue_music, color: fs.ash, size: 56),
Icon(LucideIcons.list_music, color: fs.ash, size: 56),
),
),
),
@@ -80,7 +81,7 @@ class PlaylistCard extends ConsumerWidget {
top: 2,
right: 2,
child: PopupMenuButton<String>(
icon: Icon(Icons.more_vert, color: fs.parchment, size: 18),
icon: Icon(LucideIcons.ellipsis_vertical, color: fs.parchment, size: 18),
tooltip: 'Playlist actions',
color: fs.iron,
onSelected: (_) => _refresh(context, ref),
@@ -88,7 +89,7 @@ class PlaylistCard extends ConsumerWidget {
PopupMenuItem<String>(
value: 'refresh',
child: Row(children: [
Icon(Icons.refresh, color: fs.parchment, size: 18),
Icon(LucideIcons.refresh_cw, color: fs.parchment, size: 18),
const SizedBox(width: 8),
Text(_refreshLabel,
style: TextStyle(color: fs.parchment)),
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import '../../theme/theme_extension.dart';
@@ -65,9 +66,9 @@ class PlaylistPlaceholderCard extends StatelessWidget {
child: CircularProgressIndicator(strokeWidth: 2, color: fs.accent),
);
case 'failed':
return Icon(Icons.warning_amber, color: fs.error, size: 32);
return Icon(LucideIcons.triangle_alert, color: fs.error, size: 32);
default:
return Icon(Icons.queue_music, color: fs.ash, size: 40);
return Icon(LucideIcons.list_music, color: fs.ash, size: 40);
}
}
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -32,7 +33,7 @@ class RequestsScreen extends ConsumerWidget {
backgroundColor: fs.obsidian,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: fs.parchment),
icon: Icon(LucideIcons.arrow_left, color: fs.parchment),
onPressed: () => context.pop(),
),
title: Text('Your requests', style: TextStyle(color: fs.parchment)),
@@ -130,9 +131,9 @@ class _RequestRow extends StatelessWidget {
Widget _kindAvatar(FabledSwordTheme fs) {
final icon = switch (request.kind) {
'artist' => Icons.album,
'album' => Icons.library_music,
_ => Icons.music_note,
'artist' => LucideIcons.disc_3,
'album' => LucideIcons.library_big,
_ => LucideIcons.music,
};
return CircleAvatar(
backgroundColor: fs.iron,
@@ -164,7 +165,7 @@ class _RequestRow extends StatelessWidget {
);
if (ok == true) onCancel();
},
icon: const Icon(Icons.close, size: 16),
icon: const Icon(LucideIcons.x, size: 16),
label: const Text('Cancel'),
style: TextButton.styleFrom(foregroundColor: fs.ash),
);
@@ -172,7 +173,7 @@ class _RequestRow extends StatelessWidget {
if (request.status == 'completed' && href != null) {
return TextButton.icon(
onPressed: () => context.push(href),
icon: const Icon(Icons.play_arrow, size: 16),
icon: const Icon(LucideIcons.play, size: 16),
label: const Text('Listen'),
style: TextButton.styleFrom(foregroundColor: fs.accent),
);
+3 -2
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -47,7 +48,7 @@ class _SearchScreenState extends ConsumerState<SearchScreen> {
backgroundColor: fs.obsidian,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: fs.parchment),
icon: Icon(LucideIcons.arrow_left, color: fs.parchment),
onPressed: () => context.pop(),
),
title: TextField(
@@ -67,7 +68,7 @@ class _SearchScreenState extends ConsumerState<SearchScreen> {
actions: [
if (_controller.text.isNotEmpty)
IconButton(
icon: Icon(Icons.clear, color: fs.ash),
icon: Icon(LucideIcons.x, color: fs.ash),
onPressed: () {
_controller.clear();
ref.read(searchQueryProvider.notifier).set('');
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:package_info_plus/package_info_plus.dart';
@@ -88,7 +89,7 @@ class _AboutSectionState extends ConsumerState<AboutSection> {
padding: const EdgeInsets.fromLTRB(16, 0, 16, 4),
child: Row(
children: [
Icon(Icons.info_outline, color: fs.parchment, size: 18),
Icon(LucideIcons.info, color: fs.parchment, size: 18),
const SizedBox(width: 8),
Text('Installed version',
style: TextStyle(color: fs.parchment, fontSize: 14)),
@@ -105,7 +106,7 @@ class _AboutSectionState extends ConsumerState<AboutSection> {
padding: const EdgeInsets.fromLTRB(16, 4, 16, 4),
child: Row(
children: [
Icon(Icons.cloud_outlined, color: fs.parchment, size: 18),
Icon(LucideIcons.cloud, color: fs.parchment, size: 18),
const SizedBox(width: 8),
Text('Latest version',
style: TextStyle(color: fs.parchment, fontSize: 14)),
@@ -148,7 +149,7 @@ class _AboutSectionState extends ConsumerState<AboutSection> {
valueColor: AlwaysStoppedAnimation(fs.parchment),
),
)
: const Icon(Icons.refresh, size: 18),
: const Icon(LucideIcons.refresh_cw, size: 18),
label: Text(_checking ? 'Checking…' : 'Check for updates'),
),
if (hasUpdate) ...[
@@ -172,7 +173,7 @@ class _AboutSectionState extends ConsumerState<AboutSection> {
valueColor: AlwaysStoppedAnimation(fs.parchment),
),
)
: const Icon(Icons.system_update, size: 18),
: const Icon(LucideIcons.download, size: 18),
label: Text(
_installStage == _InstallStage.downloading
? 'Downloading…'
@@ -1,5 +1,6 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -37,7 +38,7 @@ class SettingsScreen extends ConsumerWidget {
backgroundColor: fs.obsidian,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: fs.parchment),
icon: Icon(LucideIcons.arrow_left, color: fs.parchment),
onPressed: () => context.pop(),
),
title: Text('Settings', style: TextStyle(color: fs.parchment)),
@@ -107,7 +108,7 @@ class _RequestsSection extends StatelessWidget {
final fs = Theme.of(context).extension<FabledSwordTheme>()!;
return ListTile(
key: const Key('settings_requests_card'),
leading: Icon(Icons.queue_music, color: fs.parchment),
leading: Icon(LucideIcons.list_music, color: fs.parchment),
title: Text(
'My requests',
style: TextStyle(
@@ -120,7 +121,7 @@ class _RequestsSection extends StatelessWidget {
"Track what you've asked Minstrel to add",
style: TextStyle(color: fs.ash),
),
trailing: Icon(Icons.chevron_right, color: fs.ash),
trailing: Icon(LucideIcons.chevron_right, color: fs.ash),
onTap: () => context.push('/requests'),
);
}
@@ -143,7 +144,7 @@ class _AdminSection extends ConsumerWidget {
const _Divider(),
ListTile(
key: const Key('settings_admin_card'),
leading: Icon(Icons.shield, color: fs.parchment),
leading: Icon(LucideIcons.shield, color: fs.parchment),
title: Text(
'Admin',
style: TextStyle(
@@ -156,7 +157,7 @@ class _AdminSection extends ConsumerWidget {
'Manage requests, quarantine, and users',
style: TextStyle(color: fs.ash),
),
trailing: Icon(Icons.chevron_right, color: fs.ash),
trailing: Icon(LucideIcons.chevron_right, color: fs.ash),
onTap: () => context.push('/admin'),
),
],
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../cache/audio_cache_manager.dart';
@@ -140,7 +141,7 @@ class _StorageSectionState extends ConsumerState<StorageSection> {
height: 14,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.sync, size: 16),
: const Icon(LucideIcons.refresh_cw, size: 16),
label: const Text('Sync now'),
),
]),
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -26,27 +27,27 @@ class MainAppBarActions extends ConsumerWidget {
if (currentRoute != '/home')
IconButton(
key: const Key('app_bar_home'),
icon: Icon(Icons.home, color: fs.parchment),
icon: Icon(LucideIcons.house, color: fs.parchment),
tooltip: 'Home',
onPressed: () => context.go('/home'),
),
if (currentRoute != '/library')
IconButton(
key: const Key('app_bar_library'),
icon: Icon(Icons.library_music, color: fs.parchment),
icon: Icon(LucideIcons.library_big, color: fs.parchment),
tooltip: 'Library',
onPressed: () => context.push('/library'),
),
if (currentRoute != '/search')
IconButton(
key: const Key('app_bar_search'),
icon: Icon(Icons.search, color: fs.parchment),
icon: Icon(LucideIcons.search, color: fs.parchment),
tooltip: 'Search',
onPressed: () => context.push('/search'),
),
PopupMenuButton<String>(
key: const Key('app_bar_overflow'),
icon: Icon(Icons.more_vert, color: fs.parchment),
icon: Icon(LucideIcons.ellipsis_vertical, color: fs.parchment),
tooltip: 'More',
onSelected: (route) => context.push(route),
itemBuilder: (_) => [
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../playlists/playlists_provider.dart';
@@ -70,7 +71,7 @@ class AddToPlaylistSheet extends ConsumerWidget {
final p = owned[i];
return ListTile(
key: Key('add_to_playlist_${p.id}'),
leading: Icon(Icons.queue_music, color: fs.parchment),
leading: Icon(LucideIcons.list_music, color: fs.parchment),
title: Text(
p.name,
style: TextStyle(color: fs.parchment),
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import '../../../models/track.dart';
import '../../../theme/theme_extension.dart';
@@ -29,7 +30,7 @@ class TrackActionsButton extends StatelessWidget {
Widget build(BuildContext context) {
final fs = Theme.of(context).extension<FabledSwordTheme>()!;
return IconButton(
icon: Icon(Icons.more_vert, color: fs.ash, size: 18),
icon: Icon(LucideIcons.ellipsis_vertical, color: fs.ash, size: 18),
tooltip: 'Track actions',
onPressed: () => TrackActionsSheet.show(
context,
@@ -1,5 +1,6 @@
import 'package:drift/drift.dart' as drift;
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@@ -73,7 +74,7 @@ class TrackActionsSheet extends ConsumerWidget {
if (!hideQueueActions) ...[
_MenuItem(
key: const Key('track_actions_play_next'),
icon: Icons.playlist_play,
icon: LucideIcons.list_video,
label: 'Play next',
onTap: () async {
Navigator.pop(context);
@@ -87,7 +88,7 @@ class TrackActionsSheet extends ConsumerWidget {
),
_MenuItem(
key: const Key('track_actions_enqueue'),
icon: Icons.queue_music,
icon: LucideIcons.list_music,
label: 'Add to queue',
onTap: () async {
Navigator.pop(context);
@@ -103,7 +104,7 @@ class TrackActionsSheet extends ConsumerWidget {
],
_MenuItem(
key: const Key('track_actions_like'),
icon: liked ? Icons.favorite : Icons.favorite_border,
icon: LucideIcons.heart,
label: liked ? 'Unlike' : 'Like',
onTap: () async {
Navigator.pop(context);
@@ -112,7 +113,7 @@ class TrackActionsSheet extends ConsumerWidget {
),
_MenuItem(
key: const Key('track_actions_add_to_playlist'),
icon: Icons.playlist_add,
icon: LucideIcons.list_plus,
label: 'Add to playlist…',
onTap: () async {
Navigator.pop(context);
@@ -136,7 +137,7 @@ class TrackActionsSheet extends ConsumerWidget {
),
_MenuItem(
key: const Key('track_actions_start_radio'),
icon: Icons.radio,
icon: LucideIcons.radio,
label: 'Start radio',
onTap: () async {
Navigator.pop(context);
@@ -154,7 +155,7 @@ class TrackActionsSheet extends ConsumerWidget {
const _Divider(),
_MenuItem(
key: const Key('track_actions_go_to_album'),
icon: Icons.album,
icon: LucideIcons.disc_3,
label: 'Go to album',
onTap: () {
Navigator.pop(context);
@@ -168,7 +169,7 @@ class TrackActionsSheet extends ConsumerWidget {
),
_MenuItem(
key: const Key('track_actions_go_to_artist'),
icon: Icons.person,
icon: LucideIcons.user,
label: 'Go to artist',
onTap: () {
Navigator.pop(context);
@@ -183,7 +184,7 @@ class TrackActionsSheet extends ConsumerWidget {
const _Divider(),
_MenuItem(
key: const Key('track_actions_hide'),
icon: hidden ? Icons.visibility : Icons.visibility_off,
icon: hidden ? LucideIcons.eye : LucideIcons.eye_off,
label: hidden ? 'Unhide' : 'Hide',
onTap: () async {
Navigator.pop(context);
@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:pub_semver/pub_semver.dart';
@@ -238,7 +239,7 @@ class VersionTooOldBanner extends ConsumerWidget {
color: fs.error.withValues(alpha: 0.15),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Row(children: [
Icon(Icons.warning_amber_rounded, color: fs.error, size: 18),
Icon(LucideIcons.triangle_alert, color: fs.error, size: 18),
const SizedBox(width: 8),
Expanded(
child: Text(
+3 -2
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../theme/theme_extension.dart';
@@ -66,7 +67,7 @@ class _UpdateBannerState extends ConsumerState<UpdateBanner> {
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.system_update, color: fs.parchment, size: 16),
Icon(LucideIcons.download, color: fs.parchment, size: 16),
const SizedBox(width: 8),
Expanded(
child: Column(
@@ -132,7 +133,7 @@ class _UpdateBannerState extends ConsumerState<UpdateBanner> {
tooltip: 'Dismiss',
padding: EdgeInsets.zero,
iconSize: 16,
icon: Icon(Icons.close, color: fs.ash),
icon: Icon(LucideIcons.x, color: fs.ash),
onPressed: () => _onDismiss(info),
),
),