game_compass_base.dart 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';
  3. import 'package:trackoffical_app/screen.dart';
  4. import 'package:trackoffical_app/utils.dart';
  5. import 'package:vector_math/vector_math_64.dart' as vec;
  6. import '../../../widget/compass2.dart';
  7. import '../game_std/utils.dart';
  8. import '../widget_ruler2.dart';
  9. import 'game_compass_top4_data.dart';
  10. export 'game_compass_top4_data.dart';
  11. export 'game_compass_button.dart';
  12. export 'package:flutter/material.dart';
  13. class GameCompassController{
  14. final heartRatePercent = 0.0.obs;
  15. final isNoMapRulerScaleMode=false.obs;
  16. final duration = 0.seconds.obs;
  17. final compassRadians= 0.0.obs;
  18. final nextPointRadians= 0.0.obs;
  19. final heartRate = 0.obs;
  20. final stepCount=0.obs;
  21. final kCal= 0.0.obs;
  22. final ck= 0.0.obs;
  23. final ei= 0.0.obs;
  24. /// 地图比例尺 1:[userSetMapScale]
  25. final Rx<double?> userSetMapScale = Rx(null);
  26. }
  27. class GameCompassBase extends StatelessWidget{
  28. const GameCompassBase({
  29. super.key,
  30. required this.controller,
  31. required this.top4data,
  32. required this.topButtons,
  33. required this.process,
  34. this.compassDiameter=230,
  35. required this.isShowNextPointer,
  36. required this.isEnableRuler, required this.bottomCenterButton,
  37. });
  38. static const _hPadding = 60.0;
  39. final GameCompassController controller;
  40. final List<GameCompassTop4Data> top4data;
  41. final List<Widget> topButtons;
  42. final Widget? process;
  43. final double compassDiameter;
  44. final bool isShowNextPointer;
  45. final bool isEnableRuler;
  46. final Widget bottomCenterButton;
  47. @override
  48. Widget build(BuildContext context) {
  49. return Scaffold(
  50. body: DefaultTextStyle(
  51. style: const TextStyle(color: Colors.white),
  52. child: Stack(
  53. children: [
  54. Obx(() => Container(
  55. decoration: BoxDecoration(
  56. color: controller.heartRatePercent.value.round().toHRPColor()),
  57. width: context.width,
  58. height: context.height,
  59. )),
  60. Obx(() => !controller.isNoMapRulerScaleMode.value
  61. ? const Ruler2(isScaleMode: false)
  62. : const SizedBox()),
  63. Container(
  64. padding: EdgeInsets.only(
  65. top: context.mediaQueryPadding.top,
  66. left: _hPadding,
  67. right: _hPadding),
  68. width: context.width,
  69. child: Column(
  70. crossAxisAlignment: CrossAxisAlignment.center,
  71. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  72. children: [
  73. const SizedBox(height: 20),
  74. _topButtonBar(),
  75. _wDuration(),
  76. _process(),
  77. _topElemHub(),
  78. _compass(),
  79. _heartRate(),
  80. _sports(),
  81. _bottomBar(),
  82. ])),
  83. Obx(() => controller.isNoMapRulerScaleMode.value && isEnableRuler
  84. ? Ruler2(
  85. isScaleMode: true,
  86. onReturn: () =>
  87. controller.isNoMapRulerScaleMode.value = false,
  88. mapScale: controller.userSetMapScale.value)
  89. : const SizedBox(height: 0))
  90. ],
  91. ),
  92. ));
  93. }
  94. Widget _topButtonBar() {
  95. return Row(
  96. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  97. children: topButtons,
  98. );
  99. }
  100. Widget _wDuration() {
  101. return Obx(() => Text(controller.duration.value.toAppString(),
  102. maxLines: 1,
  103. style: const TextStyle(
  104. fontSize: 29,
  105. fontFamily: 'sa-digital-number',
  106. )));
  107. }
  108. Widget _process() {
  109. final w = process;
  110. return w!= null?DefaultTextStyle(style: const TextStyle(
  111. fontSize: 32,
  112. ), child: w): const SizedBox();
  113. }
  114. Widget _topElemHub() {
  115. final rows = <Widget>[];
  116. final titles = <Widget>[];
  117. final divider = SizedBox(width: 2.78.wp);
  118. rows.add(Row(
  119. children: [
  120. top4data[0],
  121. divider,
  122. top4data[1],
  123. ],
  124. ));
  125. titles.addAll([
  126. Positioned(
  127. left: 0,
  128. top: 0,
  129. child: _getSmallTitle(top4data[0].title)
  130. ),
  131. Positioned(
  132. right: 0,
  133. top: 0,
  134. child: _getSmallTitle(top4data[1].title)
  135. ),
  136. ]);
  137. if(top4data.length==4){
  138. rows.add(SizedBox(height: 2.78.wp));
  139. rows.add(Row(
  140. children: [
  141. top4data[2],
  142. divider,
  143. top4data[3],
  144. ],
  145. ));
  146. titles.addAll([
  147. Positioned(
  148. left: 0,
  149. bottom: 0,
  150. child: _getSmallTitle(top4data[2].title)
  151. ),
  152. Positioned(
  153. right: 0,
  154. bottom: 0,
  155. child: _getSmallTitle(top4data[3].title)
  156. ),
  157. ]);
  158. }
  159. final stacks = <Widget>[
  160. Column(
  161. children: rows,
  162. )
  163. ];
  164. stacks.addAll(titles);
  165. return Stack(
  166. children: stacks,
  167. ) ;
  168. }
  169. Widget _getSmallTitle(String title){
  170. return Container(
  171. alignment: Alignment.center,
  172. padding: const EdgeInsets.only(left: 3, right: 3),
  173. decoration: const BoxDecoration(color: Colors.black),
  174. child: Text(title,
  175. style: const TextStyle(
  176. fontSize:11,
  177. color: Colors.white,
  178. fontWeight: FontWeight.w700))
  179. );
  180. }
  181. Widget _compass() {
  182. return Obx(() {
  183. final radians = controller.compassRadians.value;
  184. return Compass2(
  185. compassRadians: radians,
  186. mapNorthRadians: radians,
  187. nextPointRadians: isShowNextPointer? controller.nextPointRadians.value: null,
  188. level: 1,
  189. showDegrees: vec.degrees(radians).compassDegrees,
  190. diameter: compassDiameter,
  191. nextPointImage: Image.asset('assets/images/im_compass_next_arrow2.png'),
  192. nextPointHeight: 100,
  193. degreeColor: Colors.white,
  194. plantSrc: 'assets/images/im_compass_no_map.png',
  195. );
  196. } );
  197. }
  198. Widget _heartRate() {
  199. return Obx(() => Row(
  200. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  201. children: [
  202. wHrp(),
  203. Container(
  204. width: 1,
  205. height: 53,
  206. color: Colors.white.withAlpha((255 * 0.3).round())),
  207. wHr(),
  208. ],
  209. ));
  210. }
  211. Widget wHrp() {
  212. final hrp = controller.heartRatePercent.value.round();
  213. return Row(
  214. mainAxisSize: MainAxisSize.min,
  215. crossAxisAlignment: CrossAxisAlignment.start,
  216. children: [
  217. Text('$hrp',
  218. style: const TextStyle(fontSize: 43, fontWeight: FontWeight.w700)),
  219. const Text('%',
  220. style: TextStyle(fontSize: 20, fontWeight: FontWeight.w700))
  221. ],
  222. );
  223. }
  224. Widget wHr() {
  225. return Row(
  226. mainAxisSize: MainAxisSize.min,
  227. crossAxisAlignment: CrossAxisAlignment.start,
  228. children: [
  229. Text('${controller.heartRate.value}',
  230. style: const TextStyle(fontSize: 43, fontWeight: FontWeight.w700)),
  231. Padding(
  232. padding: const EdgeInsets.only(top: 5),
  233. child: Image.asset('assets/images/ic_heart.png', height: 14.4))
  234. ],
  235. );
  236. }
  237. Widget _sports() {
  238. const style = TextStyle(fontSize: 15.24, fontWeight: FontWeight.w500);
  239. return Obx(() => Row(
  240. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  241. children: [
  242. wSport(
  243. '${controller.stepCount.value}', 'assets/images/ic_step_count.png',
  244. textStyle: style),
  245. wSport(controller.kCal.value.toStringAsFixed(1),
  246. 'assets/images/ic_kcal.png',
  247. textStyle: style),
  248. wSport(
  249. controller.ck.value.toStringAsFixed(1), 'assets/images/ic_ck.png',
  250. textStyle: style),
  251. wSport(controller.ei.round().toString(), 'assets/images/ic_ei.png',
  252. textStyle: style),
  253. ],
  254. ));
  255. }
  256. Widget _bottomBar() {
  257. return Padding(
  258. padding: const EdgeInsets.only(bottom: 20, left: 20, right: 20),
  259. child: Row(
  260. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  261. crossAxisAlignment: CrossAxisAlignment.end,
  262. children: [
  263. isEnableRuler?
  264. GestureDetector(
  265. onTap: _onSetMapScale,
  266. child: Image.asset('assets/images/btn_ruler_scale_no_map.png',
  267. width: 46,
  268. )): const SizedBox(),
  269. bottomCenterButton,
  270. isEnableRuler?
  271. GestureDetector(
  272. onTap: () => controller.isNoMapRulerScaleMode.value = true,
  273. child: Image.asset(
  274. 'assets/images/btn_ruler_no_map.png',
  275. width: 46,
  276. )): const SizedBox()
  277. ],
  278. ));
  279. }
  280. _onSetMapScale() {
  281. var value = controller.userSetMapScale.value?.toString() ?? '';
  282. Get.dialog(AlertDialog(
  283. title: const Text('设置比例尺'),
  284. content: Row(
  285. children: [
  286. const Text(
  287. '1 :',
  288. style: TextStyle(fontSize: 16),
  289. ),
  290. const SizedBox(
  291. width: 6,
  292. ),
  293. SizedBox(
  294. width: 120,
  295. child: TextField(
  296. onChanged: (v) {
  297. value = v;
  298. },
  299. keyboardType: TextInputType.number,
  300. controller: TextEditingController.fromValue(
  301. TextEditingValue(text: value)))),
  302. ],
  303. ),
  304. actions: [
  305. TextButton(
  306. onPressed: () {
  307. Get.back();
  308. },
  309. child: const Text('取消')),
  310. TextButton(
  311. onPressed: () {
  312. controller.userSetMapScale.value = double.tryParse(value)!;
  313. Get.back();
  314. },
  315. child: const Text('确定')),
  316. ],
  317. ));
  318. }
  319. }