settings_dialog.dart 9.0 KB

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