From 498fb6af8306e8d6e7ddfbbf4031ebd7961d31ac Mon Sep 17 00:00:00 2001 From: airkjw Date: Sat, 13 Jun 2026 18:01:23 +0900 Subject: [PATCH] feat(settings): sound & vibration toggle; themed settings screen Co-Authored-By: Claude Sonnet 4.6 --- lib/l10n/app_en.arb | 3 +- lib/l10n/app_ko.arb | 3 +- lib/ui/screens/settings_screen.dart | 78 ++++++++++++++++++----------- 3 files changed, 53 insertions(+), 31 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 8c2659d..eb00f48 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -59,5 +59,6 @@ "removeAdsDescription": "Removes banners and full-screen ads. Reward ads stay available.", "restorePurchases": "Restore purchases", "adsRemovedThanks": "Ads removed — thank you!", - "purchaseUnavailable": "Purchases are unavailable right now." + "purchaseUnavailable": "Purchases are unavailable right now.", + "soundAndVibration": "Sound & vibration" } diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb index a4f2705..47be69c 100644 --- a/lib/l10n/app_ko.arb +++ b/lib/l10n/app_ko.arb @@ -31,5 +31,6 @@ "removeAdsDescription": "배너와 전면 광고를 제거합니다. 보상형 광고는 계속 사용할 수 있습니다.", "restorePurchases": "구매 복원", "adsRemovedThanks": "광고가 제거되었습니다 — 감사합니다!", - "purchaseUnavailable": "지금은 구매를 사용할 수 없습니다." + "purchaseUnavailable": "지금은 구매를 사용할 수 없습니다.", + "soundAndVibration": "소리 및 진동" } diff --git a/lib/ui/screens/settings_screen.dart b/lib/ui/screens/settings_screen.dart index e4269af..cff3545 100644 --- a/lib/ui/screens/settings_screen.dart +++ b/lib/ui/screens/settings_screen.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../game/models/season.dart'; import '../../l10n/gen/app_localizations.dart'; import '../../state/providers.dart'; +import '../widgets/season_background.dart'; class SettingsScreen extends ConsumerWidget { const SettingsScreen({super.key}); @@ -12,6 +14,7 @@ class SettingsScreen extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context)!; final adsRemoved = ref.watch(adsRemovedProvider); + final soundOn = ref.watch(soundEnabledProvider); final iap = ref.read(iapServiceProvider); ref.listen(adsRemovedProvider, (prev, next) { @@ -22,37 +25,54 @@ class SettingsScreen extends ConsumerWidget { } }); - return Scaffold( - appBar: AppBar(title: Text(l10n.settings)), - body: ListView( - padding: const EdgeInsets.all(16), - children: [ - ListTile( - title: Text(l10n.removeAds), - subtitle: Text(l10n.removeAdsDescription), - trailing: adsRemoved - ? const Icon(Icons.check_circle, color: Colors.green) - : Text(iap.product?.price ?? ''), - onTap: adsRemoved - ? null - : () async { - if (!iap.available || iap.product == null) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.purchaseUnavailable)), - ); - return; - } - await iap.buyRemoveAds(); - }, + return Stack( + fit: StackFit.expand, + children: [ + const SeasonBackground(theme: SeasonTheme.fallback), + Scaffold( + backgroundColor: Colors.transparent, + appBar: AppBar( + backgroundColor: Colors.transparent, + title: Text(l10n.settings), ), - const Divider(), - ListTile( - leading: const Icon(Icons.restore), - title: Text(l10n.restorePurchases), - onTap: () => iap.restorePurchases(), + body: ListView( + padding: const EdgeInsets.all(16), + children: [ + SwitchListTile( + title: Text(l10n.soundAndVibration), + value: soundOn, + onChanged: (v) => + ref.read(soundEnabledProvider.notifier).set(v), + ), + const Divider(), + ListTile( + title: Text(l10n.removeAds), + subtitle: Text(l10n.removeAdsDescription), + trailing: adsRemoved + ? const Icon(Icons.check_circle, color: Colors.green) + : Text(iap.product?.price ?? ''), + onTap: adsRemoved + ? null + : () async { + if (!iap.available || iap.product == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(l10n.purchaseUnavailable)), + ); + return; + } + await iap.buyRemoveAds(); + }, + ), + const Divider(), + ListTile( + leading: const Icon(Icons.restore), + title: Text(l10n.restorePurchases), + onTap: () => iap.restorePurchases(), + ), + ], ), - ], - ), + ), + ], ); } }