Files
Ensemble/lib/widgets/library_stats.dart
Claude add761889b Fix multiple UI and playback issues
Added Features:
- Created NowPlayingScreen for proper remote playback control
  - Shows album artwork, track info, player controls
  - Volume control, shuffle/repeat toggles
  - Player status display
  - Queue navigation button

Fixed Loading Issues:
- Convert AlbumRow to StatefulWidget to prevent infinite reloading
  - Future now created once in initState(), not on every rebuild
  - Fixes "loading forever" issue caused by provider polling triggering rebuilds
- Convert LibraryStats to StatefulWidget with same fix
- Remove animated loading bar from mini player
  - Replaced with simple status indicator bar

Fixed Player Selection:
- Player selection now STICKY - won't auto-switch to other players
  - Once a player is selected, it stays selected
  - Only auto-selects when NO player is currently selected
  - Fixes issue where app showed wrong track info (Beatles on all speakers)
- Mini player now navigates to NowPlayingScreen instead of QueueScreen

Player State Polling:
- Continues to poll every 2 seconds for current track updates
- Properly updates selected player state
- Auto-advance should now work as Music Assistant handles it server-side

This should resolve:
1. ✅ Now playing bar opens proper player screen
2. ✅ Home page no longer loading forever
3. ✅ Wrong player/track info fixed (sticky selection)
4. ✅ Loading bar no longer "always thinking"
5. ✅ Auto-advance (Music Assistant handles this, app just polls for updates)
2025-11-23 09:56:47 +00:00

117 lines
2.8 KiB
Dart

import 'package:flutter/material.dart';
class LibraryStats extends StatefulWidget {
final Future<Map<String, int>> Function() loadStats;
const LibraryStats({
super.key,
required this.loadStats,
});
@override
State<LibraryStats> createState() => _LibraryStatsState();
}
class _LibraryStatsState extends State<LibraryStats> {
late Future<Map<String, int>> _statsFuture;
@override
void initState() {
super.initState();
_statsFuture = widget.loadStats();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<Map<String, int>>(
future: _statsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: CircularProgressIndicator(),
),
);
}
final stats = snapshot.data ?? {'artists': 0, 'albums': 0, 'tracks': 0};
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: Colors.grey[900],
borderRadius: BorderRadius.circular(12.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_StatItem(
icon: Icons.person,
label: 'Artists',
count: stats['artists'] ?? 0,
),
_StatItem(
icon: Icons.album,
label: 'Albums',
count: stats['albums'] ?? 0,
),
_StatItem(
icon: Icons.music_note,
label: 'Tracks',
count: stats['tracks'] ?? 0,
),
],
),
);
},
);
}
}
class _StatItem extends StatelessWidget {
final IconData icon;
final String label;
final int count;
const _StatItem({
required this.icon,
required this.label,
required this.count,
});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 32, color: Colors.blue[400]),
const SizedBox(height: 4),
Text(
_formatCount(count),
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 2),
Text(
label,
style: TextStyle(
fontSize: 12,
color: Colors.grey[400],
),
),
],
);
}
String _formatCount(int count) {
if (count >= 1000) {
return '${(count / 1000).toStringAsFixed(1)}k';
}
return count.toString();
}
}