📱 Responsive library screen

This commit is contained in:
2025-12-17 22:34:07 +08:00
parent 935e77421e
commit 6bc8946fae

View File

@@ -12,6 +12,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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'package:styled_widget/styled_widget.dart';
class LibraryScreen extends HookConsumerWidget { class LibraryScreen extends HookConsumerWidget {
const LibraryScreen({super.key}); const LibraryScreen({super.key});
@@ -49,6 +50,7 @@ class LibraryScreen extends HookConsumerWidget {
final searchQuery = useState<String>(''); final searchQuery = useState<String>('');
final isSelectionMode = selectedTrackIds.value.isNotEmpty; final isSelectionMode = selectedTrackIds.value.isNotEmpty;
final isLargeScreen = MediaQuery.of(context).size.width > 600; final isLargeScreen = MediaQuery.of(context).size.width > 600;
final isExtraLargeScreen = MediaQuery.of(context).size.width > 800;
final selectedTab = isLargeScreen ? useState<int>(0) : null; final selectedTab = isLargeScreen ? useState<int>(0) : null;
void toggleSelection(int id) { void toggleSelection(int id) {
@@ -104,8 +106,20 @@ class LibraryScreen extends HookConsumerWidget {
], ],
) )
: AppBar( : AppBar(
centerTitle: true, title: isLargeScreen
title: const Text('Library'), ? Row(
children: [
const Gap(4),
Image.asset(
'assets/images/icon.jpg',
width: 32,
height: 32,
).clipRRect(all: 8).padding(vertical: 16),
const Gap(12),
Text('GroovyBox', style: TextStyle(fontSize: 18)),
],
)
: const Text('Library'),
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Icons.add_circle_outline), icon: const Icon(Icons.add_circle_outline),
@@ -144,6 +158,7 @@ class LibraryScreen extends HookConsumerWidget {
} }
// Import lyrics if any // Import lyrics if any
if (!context.mounted) return;
if (lyricsPaths.isNotEmpty) { if (lyricsPaths.isNotEmpty) {
await _batchImportLyricsFromPaths( await _batchImportLyricsFromPaths(
context, context,
@@ -158,9 +173,14 @@ class LibraryScreen extends HookConsumerWidget {
const Gap(8), const Gap(8),
], ],
), ),
body: Row( body: Column(
children: [
const Divider(height: 1),
Expanded(
child: Row(
children: [ children: [
NavigationRail( NavigationRail(
extended: isExtraLargeScreen,
selectedIndex: selectedTab!.value, selectedIndex: selectedTab!.value,
onDestinationSelected: (index) => selectedTab.value = index, onDestinationSelected: (index) => selectedTab.value = index,
destinations: const [ destinations: const [
@@ -178,6 +198,7 @@ class LibraryScreen extends HookConsumerWidget {
), ),
], ],
), ),
const VerticalDivider(width: 1),
Expanded( Expanded(
child: _buildTabContent( child: _buildTabContent(
selectedTab.value, selectedTab.value,
@@ -191,6 +212,9 @@ class LibraryScreen extends HookConsumerWidget {
), ),
], ],
), ),
),
],
),
); );
} else { } else {
return DefaultTabController( return DefaultTabController(
@@ -364,25 +388,12 @@ class LibraryScreen extends HookConsumerWidget {
return const Center(child: Text('No tracks match your search.')); return const Center(child: Text('No tracks match your search.'));
} }
return Column( return Stack(
children: [ children: [
Padding( ListView.builder(
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * 0.04,
vertical: 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,
top: 80,
), ),
itemCount: filteredTracks.length, itemCount: filteredTracks.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@@ -431,13 +442,11 @@ class LibraryScreen extends HookConsumerWidget {
), ),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => onPressed: () => Navigator.of(context).pop(false),
Navigator.of(context).pop(false),
child: const Text('Cancel'), child: const Text('Cancel'),
), ),
TextButton( TextButton(
onPressed: () => onPressed: () => Navigator.of(context).pop(true),
Navigator.of(context).pop(true),
style: TextButton.styleFrom( style: TextButton.styleFrom(
foregroundColor: Colors.red, foregroundColor: Colors.red,
), ),
@@ -508,6 +517,21 @@ class LibraryScreen extends HookConsumerWidget {
); );
}, },
), ),
Positioned(
top: 0,
left: 0,
right: 0,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: SearchBar(
onChanged: (value) => searchQuery.value = value,
hintText: 'Search tracks...',
leading: const Icon(Icons.search),
padding: WidgetStatePropertyAll(
EdgeInsets.symmetric(horizontal: 24),
),
),
),
), ),
], ],
); );