feat(audio): looping per-season background music system
MusicService (looping audioplayers player, independent of SFX) driven by the active season theme's new 'bgm' key; switches track on season change, pauses on app background, all failures swallowed. Separate Music on/off toggle in Settings (persisted, independent of SFX). Season packs carry bgm keys (menu/season_001/ season_002), manifest regenerated. Assets slot assets/audio/bgm/ ready — drop in menu.mp3/season_001.mp3/season_002.mp3 (CC0) and it plays; silent until then. 180 tests green, analyze clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+24
-1
@@ -13,21 +13,44 @@ class BlockSeasonsApp extends ConsumerStatefulWidget {
|
||||
ConsumerState<BlockSeasonsApp> createState() => _BlockSeasonsAppState();
|
||||
}
|
||||
|
||||
class _BlockSeasonsAppState extends ConsumerState<BlockSeasonsApp> {
|
||||
class _BlockSeasonsAppState extends ConsumerState<BlockSeasonsApp>
|
||||
with WidgetsBindingObserver {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
ref.read(consentServiceProvider).ensureConsentAndInitialize();
|
||||
// Eagerly start the IAP service so its purchase stream is live for the
|
||||
// whole session — restores and interrupted/deferred transactions are
|
||||
// delivered (and completed) even if the player never opens Settings.
|
||||
ref.read(iapServiceProvider);
|
||||
// Start background music for the current context (menu by default).
|
||||
ref.read(musicServiceProvider).playKey(ref.read(activeThemeProvider).bgm);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
// Pause music while the app is not in the foreground.
|
||||
ref
|
||||
.read(musicServiceProvider)
|
||||
.setBackgrounded(state != AppLifecycleState.resumed);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Switch the looping track whenever the active season theme changes
|
||||
// (home/menu -> a season -> back). playKey is a no-op if unchanged.
|
||||
ref.listen(activeThemeProvider, (_, next) {
|
||||
ref.read(musicServiceProvider).playKey(next.bgm);
|
||||
});
|
||||
return MaterialApp(
|
||||
onGenerateTitle: (context) => AppLocalizations.of(context)!.appTitle,
|
||||
debugShowCheckedModeBanner: false,
|
||||
|
||||
Reference in New Issue
Block a user