settings_dialog.dart 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import 'dart:math';
  2. import 'package:flutter/material.dart';
  3. import 'package:puzzleweave/audio/jc_audio_controller.dart';
  4. import 'package:puzzleweave/l10n/app_localizations.dart';
  5. import 'package:puzzleweave/models/items.dart';
  6. import 'package:puzzleweave/play/board_play.dart';
  7. import 'package:puzzleweave/settings/settings_controller.dart';
  8. import 'package:puzzleweave/skin/skin.dart';
  9. import 'package:puzzleweave/utils/mybutton.dart';
  10. import 'package:provider/provider.dart';
  11. class SettingsDialog extends StatefulWidget {
  12. final ListItem? item;
  13. final bool showReturn;
  14. final bool showRestart;
  15. const SettingsDialog({super.key, required this.showReturn, required this.showRestart, required this.item});
  16. @override
  17. State<StatefulWidget> createState() => _SettingDialogState();
  18. static PageRouteBuilder buildRoute({bool showReturn = false, bool showRestart = false, ListItem? item}) {
  19. return PageRouteBuilder(
  20. opaque: false, // 不遮盖原来的图层
  21. pageBuilder: (context, animation, secondaryAnimation) {
  22. return SettingsDialog(showReturn: showReturn, showRestart: showRestart, item: item);
  23. },
  24. transitionsBuilder: (context, animation, secondaryAnimation, child) {
  25. return FadeTransition(opacity: animation, child: child);
  26. },
  27. );
  28. }
  29. }
  30. class _SettingDialogState extends State<SettingsDialog> {
  31. @override
  32. Widget build(BuildContext context) {
  33. final settings = context.watch<SettingsController>();
  34. final audio = context.read<JcAudioController>();
  35. Widget gap = const SizedBox(height: 10);
  36. // 背景色(开启/关闭状态)
  37. const activeBgColor = Colors.white;
  38. const inactiveBgColor = Color.fromARGB(255, 205, 195, 195);
  39. // 图标颜色(固定,与背景形成对比)
  40. final iconColor = SkinHelper.slotBorderColor;
  41. return Scaffold(
  42. // backgroundColor: SkinHelper.coreBgColor,
  43. backgroundColor: Colors.black.withAlpha(128),
  44. body: Center(
  45. child: LayoutBuilder(
  46. builder: (context, constraints) {
  47. const double maxContainerWidth = 500; // 设定一个对话框最大宽度,避免在pad上显示过宽过大不好看
  48. final containerWidth = min(constraints.biggest.shortestSide * 0.75, maxContainerWidth);
  49. final buttonWidth = containerWidth * 0.8;
  50. return Container(
  51. width: containerWidth,
  52. decoration: BoxDecoration(color: SkinHelper.coreBgColor, borderRadius: const BorderRadius.all(Radius.circular(16))),
  53. child: Column(
  54. mainAxisSize: MainAxisSize.min,
  55. mainAxisAlignment: MainAxisAlignment.center,
  56. children: [
  57. // 标题栏(关闭按钮 + 标题)
  58. Row(
  59. mainAxisSize: MainAxisSize.max,
  60. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  61. children: [
  62. IconButton(
  63. icon: const Icon(Icons.close_rounded, color: Colors.white),
  64. onPressed: () {
  65. audio.playSfx(SfxType.click);
  66. Navigator.pop(context);
  67. },
  68. ),
  69. Text(
  70. AppLocalizations.of(context)!.setting,
  71. style: TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold),
  72. ),
  73. const SizedBox(width: 48), // 占位,与左侧关闭按钮平衡
  74. ],
  75. ),
  76. gap,
  77. gap,
  78. // 重新开始按钮
  79. if (widget.showRestart)
  80. MyElevatedButton(
  81. onPressed: () {
  82. audio.playSfx(SfxType.click);
  83. PageRouteBuilder? pageRouteBuilder = BoardPlay.buildRoute(widget.item!, reset: true);
  84. Navigator.pop(context);
  85. Navigator.pushReplacement(context, pageRouteBuilder);
  86. },
  87. width: buttonWidth,
  88. borderRadius: BorderRadius.circular(20),
  89. gradient: const LinearGradient(colors: [Colors.white, Colors.white]),
  90. child: Row(
  91. mainAxisAlignment: MainAxisAlignment.center,
  92. children: [
  93. Icon(Icons.restart_alt_rounded, size: 24, weight: 600, color: SkinHelper.slotBorderColor),
  94. const SizedBox(width: 5),
  95. Text(AppLocalizations.of(context)!.restart, style: TextStyle(color: SkinHelper.slotBorderColor, fontSize: 18)),
  96. ],
  97. ),
  98. ),
  99. if (widget.showReturn) gap,
  100. if (widget.showReturn) gap,
  101. // 回到主页按钮
  102. if (widget.showReturn)
  103. MyElevatedButton(
  104. onPressed: () {
  105. audio.playSfx(SfxType.click);
  106. Navigator.pop(context);
  107. Navigator.pop(context);
  108. },
  109. width: buttonWidth,
  110. borderRadius: BorderRadius.circular(20),
  111. gradient: const LinearGradient(colors: [Colors.white, Colors.white]),
  112. child: Row(
  113. mainAxisAlignment: MainAxisAlignment.center,
  114. children: [
  115. Icon(Icons.arrow_back, size: 24, weight: 600, color: SkinHelper.slotBorderColor),
  116. const SizedBox(width: 5),
  117. Text(AppLocalizations.of(context)!.backToHome, style: TextStyle(color: SkinHelper.slotBorderColor, fontSize: 18)),
  118. ],
  119. ),
  120. ),
  121. gap,
  122. gap,
  123. gap,
  124. // 三个设置项(用ValueListenableBuilder监听状态变化)
  125. Padding(
  126. padding: const EdgeInsets.symmetric(horizontal: 20),
  127. child: Row(
  128. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  129. children: [
  130. // 1. 背景音乐设置(监听music状态)
  131. ValueListenableBuilder<bool>(
  132. valueListenable: settings.music, // 监听music的变化
  133. builder: (context, isMusicOn, child) {
  134. // 状态变化时,自动重建此按钮
  135. return _buildSettingButton(
  136. icon: isMusicOn ? Icons.music_note : Icons.music_off,
  137. bgColor: isMusicOn ? activeBgColor : inactiveBgColor,
  138. iconColor: iconColor,
  139. onPressed: () {
  140. audio.playSfx(SfxType.click);
  141. settings.toggleMusic();
  142. },
  143. );
  144. },
  145. ),
  146. // 2. 音效设置(监听sound状态)
  147. ValueListenableBuilder<bool>(
  148. valueListenable: settings.sound, // 监听sound的变化
  149. builder: (context, isSoundOn, child) {
  150. return _buildSettingButton(
  151. icon: isSoundOn ? Icons.volume_up : Icons.volume_off,
  152. bgColor: isSoundOn ? activeBgColor : inactiveBgColor,
  153. iconColor: iconColor,
  154. onPressed: () {
  155. audio.playSfx(SfxType.click);
  156. settings.toggleSound();
  157. },
  158. );
  159. },
  160. ),
  161. // 3. 震动设置(监听vibrate状态)
  162. ValueListenableBuilder<bool>(
  163. valueListenable: settings.vibrate, // 监听vibrate的变化
  164. builder: (context, isVibrateOn, child) {
  165. return _buildSettingButton(
  166. icon: isVibrateOn ? Icons.vibration : Icons.crop_portrait,
  167. bgColor: isVibrateOn ? activeBgColor : inactiveBgColor,
  168. iconColor: iconColor,
  169. onPressed: () {
  170. audio.playSfx(SfxType.click);
  171. settings.toggleVibrate();
  172. },
  173. );
  174. },
  175. ),
  176. ],
  177. ),
  178. ),
  179. gap,
  180. gap,
  181. gap,
  182. ],
  183. ),
  184. );
  185. },
  186. ),
  187. ),
  188. );
  189. }
  190. // 封装设置项按钮(无需额外key,由ValueListenableBuilder保证重绘)
  191. Widget _buildSettingButton({required IconData icon, required Color bgColor, required Color iconColor, required VoidCallback onPressed}) {
  192. return InkWell(
  193. // 圆形点击区域
  194. borderRadius: BorderRadius.circular(30),
  195. onTap: onPressed,
  196. child: Container(
  197. width: 56,
  198. height: 56,
  199. decoration: BoxDecoration(color: bgColor, shape: BoxShape.circle),
  200. child: Icon(icon, size: 28, color: iconColor),
  201. ),
  202. );
  203. }
  204. }