- Make QueueManagerService optional in PlayerProvider to break circular dependency - Set queueManager after initialization in main.dart - Add castToSendspinIdMap getter to PlayerProvider for external access - Wire up QueueManagerService with proper callbacks to provider state This allows providers to be properly initialized without circular references while maintaining the ability to access shared state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7.0 KiB
7.0 KiB
Multi-Provider Split Plan
Current Status (2026-02-07)
Completed
| Provider | Lines | File | Status |
|---|---|---|---|
| ConnectionProvider | 245 | lib/providers/connection_provider.dart | ✅ Created |
| UIStateProvider | 167 | lib/providers/ui_state_provider.dart | ✅ Created & Committed |
| LibraryProvider | 290 | lib/providers/library_provider.dart | ✅ Created & Committed |
| PlayerProvider | 589 | lib/providers/player_provider.dart | ✅ Created & Integrated |
| LocalPlayerProvider | 370 | lib/providers/local_player_provider.dart | ✅ Created & Integrated |
Main Provider: 5928 lines (down from 6180 original)
Integration Status
✅ Phase 1 Complete: All 5 providers created ✅ Phase 2 Started: Integration into main.dart completed
- All providers added to MultiProvider tree
- Shared services (CacheService, PositionTracker, ImageHelperService) properly initialized
- Build successful (52.2MB APK)
Remaining Work
| Task | Complexity | Notes |
|---|---|---|
| Update MusicAssistantProvider facade | Medium | Accept providers in constructor, delegate methods |
| Migrate consumers incrementally | High | Start with less critical screens |
| Remove facade when complete | Low | After all consumers migrated |
Architecture Overview
┌─────────────────────────────────────────────────────────────────────┐
│ MusicAssistantProvider │
│ (Facade - during transition) │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ Delegates to sub-providers: │ │
│ │ • ConnectionProvider - connect/disconnect, auth │ │
│ │ • UIStateProvider - search, filters, loading state │ │
│ │ • LibraryProvider - artists, albums, tracks, podcasts │ │
│ │ • PlayerProvider - player selection, controls, queue (TODO) │ │
│ │ • LocalPlayerProvider - local audio engine (TODO) │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Next Steps
Phase 1: Complete Provider Creation (Current)
-
Create PlayerProvider (~800 lines)
- Extract player selection and switching logic
- Extract play/pause/stop/seek controls
- Extract queue management
- Extract sleep timer functionality
-
Create LocalPlayerProvider (~500 lines)
- Extract local audio playback engine
- Extract Sendspin integration
- Extract player registration
- Extract volume and power control
Phase 2: Integration
-
Update main.dart
- Add all providers to MultiProvider
- Keep MusicAssistantProvider as facade initially
-
Migrate one consumer at a time
- Start with less critical screens
- Test thoroughly after each migration
- Use facade to maintain compatibility during transition
-
Remove facade
- Once all consumers migrated
- Remove MusicAssistantProvider
- Direct widget-provider relationships
PlayerProvider Extraction Plan
State to Extract
// Player selection
Player? _selectedPlayer;
List<Player> _availablePlayers = [];
Map<String, String> _castToSendspinIdMap = {};
// Current playback
Track? _currentTrack;
Audiobook? _currentAudiobook;
String? _currentPodcastName;
// Sleep timer
Timer? _sleepTimer;
DateTime? _sleepTimerEndTime;
int? _sleepTimerMinutes;
Timer? _sleepTimerDisplayTimer;
// Timers
Timer? _playerStateTimer;
Timer? _notificationPositionTimer;
Timer? _idleServiceTimer;
// Position tracking
PositionTracker _positionTracker;
Methods to Extract
// Player selection
Future<void> _loadAndSelectPlayers({bool forceRefresh, bool coldStart});
Future<void> selectPlayer(Player player, {bool? sendToSendspin});
// Playback controls
Future<void> playPause();
Future<void> stop();
Future<void> nextTrack();
Future<void> previousTrack();
Future<void> seek(Duration position);
Future<void> setVolume(String playerId, int volumeLevel);
// Queue management
Future<PlayerQueue?> getQueue(String playerId);
Future<void> playTracks(String playerId, List<Track> tracks, {int? startIndex});
// Sleep timer
Future<void> setSleepTimer(int minutes);
Future<void> cancelSleepTimer();
Dependencies to Inject
- MusicAssistantAPI
- DatabaseService
- SettingsService
- QueueManagerService
- PositionTracker
- PlayerSyncState
- AudioHandler (from audio_service)
Estimated Timeline
| Task | Time | Notes |
|---|---|---|
| Create PlayerProvider | 2-3 hours | Complex, many dependencies |
| Create LocalPlayerProvider | 2-3 hours | Sendspin integration is complex |
| Integration & Testing | 3-4 hours | Update main.dart, migrate consumers |
| Bug Fixes | 1-2 hours | Edge cases in player switching |
Total: ~8-12 hours of focused development
Risk Mitigation
- Keep facade during transition - Maintain backward compatibility
- Migrate incrementally - One screen/consumer at a time
- Test on device - Test player controls thoroughly
- Commit frequently - Each provider as it's completed
- Keep backup - Can revert to monolithic provider if needed
Success Criteria
- All 5 providers created and building
- Main.dart updated with all providers
- At least 3 screens migrated to new providers
- Player controls working correctly on device
- No regressions in core functionality
- Main provider reduced to < 1000 lines (or removed)
Files Created So Far
lib/providers/
├── connection_provider.dart (245 lines) ✅
├── ui_state_provider.dart (167 lines) ✅
├── library_provider.dart (290 lines) ✅
├── player_provider.dart (TODO) ⏳
└── local_player_provider.dart (TODO) ⏳
Notes from Analysis
- Player selection logic is complex - Involves Cast-to-Sendspin mapping, filtering, smart sorting
- Sendspin integration is tricky - Need to handle both MA 2.7.0+ (Sendspin) and older versions
- Queue management already extracted - QueueManagerService handles most queue logic
- Position tracking is separate - PositionTracker service handles interpolation
- Event handlers need coordination - Player events update both provider and position tracker