feat(settings): soundEnabled provider gates SFX and haptics
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import '../services/consent_service.dart';
|
|||||||
import '../services/iap_service.dart';
|
import '../services/iap_service.dart';
|
||||||
import 'ads_notifier.dart';
|
import 'ads_notifier.dart';
|
||||||
import 'endless_best_notifier.dart';
|
import 'endless_best_notifier.dart';
|
||||||
|
import 'sound_notifier.dart';
|
||||||
import 'game_session_notifier.dart';
|
import 'game_session_notifier.dart';
|
||||||
import 'progress_notifier.dart';
|
import 'progress_notifier.dart';
|
||||||
import 'season_flow_notifier.dart';
|
import 'season_flow_notifier.dart';
|
||||||
@@ -22,8 +23,12 @@ final gameSessionProvider =
|
|||||||
GameSessionNotifier.new,
|
GameSessionNotifier.new,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final soundEnabledProvider =
|
||||||
|
NotifierProvider<SoundEnabledNotifier, bool>(SoundEnabledNotifier.new);
|
||||||
|
|
||||||
final audioServiceProvider = Provider<AudioService>((ref) {
|
final audioServiceProvider = Provider<AudioService>((ref) {
|
||||||
final service = AudioService();
|
final service = AudioService(enabled: ref.read(soundEnabledProvider));
|
||||||
|
ref.listen<bool>(soundEnabledProvider, (_, next) => service.enabled = next);
|
||||||
ref.onDispose(service.dispose);
|
ref.onDispose(service.dispose);
|
||||||
return service;
|
return service;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
import 'providers.dart';
|
||||||
|
|
||||||
|
/// SFX + gameplay haptics on/off, seeded from the save repository.
|
||||||
|
class SoundEnabledNotifier extends Notifier<bool> {
|
||||||
|
@override
|
||||||
|
bool build() => ref.read(saveRepositoryProvider).soundEnabled;
|
||||||
|
|
||||||
|
Future<void> toggle() => set(!state);
|
||||||
|
|
||||||
|
Future<void> set(bool value) async {
|
||||||
|
if (state == value) return;
|
||||||
|
await ref.read(saveRepositoryProvider).setSoundEnabled(value);
|
||||||
|
state = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -120,16 +120,17 @@ class _GameScreenState extends ConsumerState<GameScreen>
|
|||||||
final audio = ref.read(audioServiceProvider);
|
final audio = ref.read(audioServiceProvider);
|
||||||
if (prev?.fxTick != next.fxTick && next.lastPlacement != null) {
|
if (prev?.fxTick != next.fxTick && next.lastPlacement != null) {
|
||||||
final placement = next.lastPlacement!;
|
final placement = next.lastPlacement!;
|
||||||
|
final hapticsOn = ref.read(soundEnabledProvider);
|
||||||
if (placement.linesCleared > 0) {
|
if (placement.linesCleared > 0) {
|
||||||
audio.play(placement.comboStreak >= 2 ? Sfx.combo : Sfx.clear);
|
audio.play(placement.comboStreak >= 2 ? Sfx.combo : Sfx.clear);
|
||||||
HapticFeedback.mediumImpact();
|
if (hapticsOn) HapticFeedback.mediumImpact();
|
||||||
if (placement.comboStreak >= 4) {
|
if (placement.comboStreak >= 4) {
|
||||||
HapticFeedback.heavyImpact();
|
if (hapticsOn) HapticFeedback.heavyImpact();
|
||||||
_shake.forward(from: 0);
|
_shake.forward(from: 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
audio.play(Sfx.place);
|
audio.play(Sfx.place);
|
||||||
HapticFeedback.lightImpact();
|
if (hapticsOn) HapticFeedback.lightImpact();
|
||||||
}
|
}
|
||||||
ref.read(tutorialProvider.notifier).onPlaced();
|
ref.read(tutorialProvider.notifier).onPlaced();
|
||||||
if (placement.linesCleared > 0) {
|
if (placement.linesCleared > 0) {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import 'package:block_seasons/data/save_repository.dart';
|
||||||
|
import 'package:block_seasons/state/providers.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('reads persisted sound flag and toggles + persists', () async {
|
||||||
|
SharedPreferences.setMockInitialValues({});
|
||||||
|
final repo = await SaveRepository.open();
|
||||||
|
final c = ProviderContainer(
|
||||||
|
overrides: [saveRepositoryProvider.overrideWithValue(repo)],
|
||||||
|
);
|
||||||
|
addTearDown(c.dispose);
|
||||||
|
|
||||||
|
expect(c.read(soundEnabledProvider), isTrue);
|
||||||
|
await c.read(soundEnabledProvider.notifier).toggle();
|
||||||
|
expect(c.read(soundEnabledProvider), isFalse);
|
||||||
|
expect(repo.soundEnabled, isFalse);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user