Library able to search tracks

This commit is contained in:
2025-12-17 00:03:16 +08:00
parent f436a8a49b
commit c1652e6743

View File

@@ -46,6 +46,7 @@ class LibraryScreen extends HookConsumerWidget {
// Let's assume we use StreamBuilder for now to avoid creating another file/provider on the fly. // Let's assume we use StreamBuilder for now to avoid creating another file/provider on the fly.
final repo = ref.watch(trackRepositoryProvider.notifier); final repo = ref.watch(trackRepositoryProvider.notifier);
final selectedTrackIds = useState<Set<int>>({}); final selectedTrackIds = useState<Set<int>>({});
final searchQuery = useState<String>('');
final isSelectionMode = selectedTrackIds.value.isNotEmpty; final isSelectionMode = selectedTrackIds.value.isNotEmpty;
void toggleSelection(int id) { void toggleSelection(int id) {
@@ -180,13 +181,60 @@ class LibraryScreen extends HookConsumerWidget {
return const Center(child: Text('No tracks yet. Add some!')); return const Center(child: Text('No tracks yet. Add some!'));
} }
return ListView.builder( List<Track> filteredTracks;
if (searchQuery.value.isEmpty) {
filteredTracks = tracks;
} else {
final query = searchQuery.value.toLowerCase();
filteredTracks = tracks.where((track) {
if (track.title.toLowerCase().contains(query)) return true;
if (track.artist?.toLowerCase().contains(query) ?? false)
return true;
if (track.album?.toLowerCase().contains(query) ?? false)
return true;
if (track.lyrics != null) {
try {
final lyricsData = LyricsData.fromJsonString(
track.lyrics!,
);
for (final line in lyricsData.lines) {
if (line.text.toLowerCase().contains(query))
return true;
}
} catch (e) {
// Ignore parsing errors
}
}
return false;
}).toList();
}
if (filteredTracks.isEmpty && searchQuery.value.isNotEmpty) {
return const Center(
child: Text('No tracks match your search.'),
);
}
return Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
onChanged: (value) => searchQuery.value = value,
decoration: const InputDecoration(
hintText: 'Search tracks...',
prefixIcon: Icon(Icons.search),
),
),
),
Expanded(
child: ListView.builder(
padding: EdgeInsets.only( padding: EdgeInsets.only(
bottom: 72 + MediaQuery.paddingOf(context).bottom, bottom: 72 + MediaQuery.paddingOf(context).bottom,
), ),
itemCount: tracks.length, itemCount: filteredTracks.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final track = tracks[index]; final track = filteredTracks[index];
final isSelected = selectedTrackIds.value.contains( final isSelected = selectedTrackIds.value.contains(
track.id, track.id,
); );
@@ -220,7 +268,10 @@ 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(
Icons.delete,
color: Colors.white,
),
), ),
confirmDismiss: (direction) async { confirmDismiss: (direction) async {
return await showDialog( return await showDialog(
@@ -255,7 +306,9 @@ class LibraryScreen extends HookConsumerWidget {
.read(trackRepositoryProvider.notifier) .read(trackRepositoryProvider.notifier)
.deleteTrack(track.id); .deleteTrack(track.id);
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Deleted "${track.title}"')), SnackBar(
content: Text('Deleted "${track.title}"'),
),
); );
}, },
child: ListTile( child: ListTile(
@@ -267,7 +320,9 @@ class LibraryScreen extends HookConsumerWidget {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
image: track.artUri != null image: track.artUri != null
? DecorationImage( ? DecorationImage(
image: FileImage(File(track.artUri!)), image: FileImage(
File(track.artUri!),
),
fit: BoxFit.cover, fit: BoxFit.cover,
) )
: null, : null,
@@ -309,6 +364,9 @@ class LibraryScreen extends HookConsumerWidget {
), ),
); );
}, },
),
),
],
); );
}, },
), ),