AddScheduledExercisePage.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_habit/common/I18N.dart';
  3. import 'package:flutter_habit/common/components/PopMenus.dart';
  4. import 'package:flutter_habit/provider/DataProvider.dart';
  5. import 'package:flutter_habit/database/entity/ScheduledExercise.dart';
  6. import 'package:flutter_habit/database/entity/SportInfo.dart';
  7. import 'package:flutter_habit/database/mapper/ScheduledExerciseMapper.dart';
  8. import 'package:flutter_habit/database/mapper/SportInfoMapper.dart';
  9. import 'package:provider/provider.dart';
  10. class AddScheduledExercisePage extends StatefulWidget {
  11. @override
  12. _AddScheduledExercisePageState createState() =>
  13. _AddScheduledExercisePageState();
  14. }
  15. class _AddScheduledExercisePageState extends State<AddScheduledExercisePage> {
  16. List<SportInfo>? spots;
  17. @override
  18. void initState() {
  19. super.initState();
  20. spots = [];
  21. loadData();
  22. }
  23. Future<void> loadData() async {
  24. spots = await SportInfoMapper()
  25. .selectAll(orderBy: "sportTimes desc,name asc, hkCalorie asc");
  26. setState(() {});
  27. }
  28. Future<void> onSelected(SportInfo sportInfo) async {
  29. TextEditingController intakeController = TextEditingController();
  30. String? res = await PopMenus.baseMenu(
  31. context: context,
  32. title: Text(sportInfo.getName()!),
  33. children: <Widget>[
  34. Divider(),
  35. Text("${I18N.of("运动次数")}: ${sportInfo.getSportTimes()}"),
  36. Text("${I18N.of("消耗热量")}: ${sportInfo.getHkCalorie()} (h/kcal)"),
  37. TextFormField(
  38. keyboardType: TextInputType.number,
  39. controller: intakeController,
  40. decoration: InputDecoration(
  41. icon: Icon(Icons.timer),
  42. labelText: "${I18N.of("计划运动时长")} (min)",
  43. hintText: I18N.of("请输入计划运动时长"),
  44. ),
  45. ),
  46. TextButton(
  47. child: Text(I18N.of("确定")),
  48. onPressed: () async {
  49. if (intakeController.text.trim().isNotEmpty &&
  50. double.tryParse(intakeController.text.trim()) != null) {
  51. // 更新次数
  52. SportInfo s = SportInfo();
  53. s.setId(sportInfo.getId());
  54. s.setSportTimes(sportInfo.getSportTimes()! + 1);
  55. await SportInfoMapper().updateByFirstKeySelective(s);
  56. // 插入计划
  57. ScheduledExercise scheduledExercise = ScheduledExercise();
  58. scheduledExercise.setSportId(sportInfo.getId());
  59. scheduledExercise.setQuantity(double.parse(intakeController.text.trim()) / 60);
  60. await ScheduledExerciseMapper().insert(scheduledExercise);
  61. await PopMenus.attention(context: context, content: Text(I18N.of("添加计划任务成功")));
  62. Navigator.of(context).pop("ok");
  63. } else {
  64. await PopMenus.attention(
  65. context: context, content: Text(I18N.of("输入有误")));
  66. }
  67. },
  68. ),
  69. ],
  70. contentPadding: EdgeInsets.all(16),
  71. );
  72. if (res == "ok") {
  73. await Provider.of<DataProvider>(context,listen: false).loadExerciseInfoData();
  74. Navigator.of(context).pop();
  75. }
  76. }
  77. @override
  78. Widget build(BuildContext context) {
  79. return Scaffold(
  80. appBar: AppBar(
  81. title: Text(I18N.of("添加计划任务")),
  82. actions: <Widget>[
  83. IconButton(
  84. icon: Icon(Icons.search),
  85. onPressed: () async {
  86. String? name = await showSearch<String?>(
  87. context: context,
  88. delegate: _SportSearchDelegate(spots),
  89. );
  90. await loadData();
  91. List<SportInfo> list =
  92. spots!.where((test) => test.getName() == name).toList();
  93. if (list.isNotEmpty) {
  94. await onSelected(list.last);
  95. }
  96. },
  97. ),
  98. ],
  99. ),
  100. body: Padding(
  101. padding: EdgeInsets.all(16),
  102. child: Column(
  103. children: <Widget>[
  104. ListTile(
  105. leading: Icon(Icons.directions_run),
  106. title: Text(I18N.of("添加运动")),
  107. trailing: Icon(Icons.add),
  108. onTap: () async {
  109. TextEditingController nameController =
  110. TextEditingController();
  111. TextEditingController gkCalorieController =
  112. TextEditingController();
  113. await PopMenus.baseMenu<String>(
  114. context: context,
  115. title: Text(I18N.of("添加运动")),
  116. children: <Widget>[
  117. TextFormField(
  118. controller: nameController,
  119. decoration: InputDecoration(
  120. icon: Icon(Icons.fitness_center),
  121. labelText: I18N.of("运动名称"),
  122. hintText: I18N.of("请输入运动名称"),
  123. ),
  124. ),
  125. TextFormField(
  126. keyboardType: TextInputType.number,
  127. controller: gkCalorieController,
  128. decoration: InputDecoration(
  129. icon: Icon(Icons.whatshot),
  130. labelText: "${I18N.of("消耗热量")} (h/kcal)",
  131. hintText: I18N.of("请输入消耗热量"),
  132. ),
  133. ),
  134. TextButton(
  135. child: Text(I18N.of("确定")),
  136. onPressed: () async {
  137. if (nameController.text.trim().isNotEmpty &&
  138. gkCalorieController.text.trim().isNotEmpty &&
  139. double.tryParse(gkCalorieController.text.trim()) !=
  140. null) {
  141. SportInfo sportInfo = SportInfo();
  142. sportInfo.setName(nameController.text.trim());
  143. sportInfo.setHkCalorie(
  144. double.parse(gkCalorieController.text.trim()));
  145. sportInfo.setSportTimes(0);
  146. bool isSuccess =
  147. await SportInfoMapper().insert(sportInfo);
  148. if (isSuccess) {
  149. await loadData();
  150. Navigator.of(context).pop();
  151. } else {
  152. await PopMenus.attention(
  153. context: context,
  154. content: Text(I18N.of("该运动已存在")));
  155. }
  156. } else {
  157. await PopMenus.attention(
  158. context: context, content: Text(I18N.of("输入有误")));
  159. }
  160. },
  161. ),
  162. ],
  163. contentPadding: EdgeInsets.all(16),
  164. );
  165. },
  166. ),
  167. Divider(),
  168. Text(
  169. I18N.of("长按可删除运动"),
  170. style: Theme.of(context).textTheme.bodySmall,
  171. ),
  172. Expanded(
  173. child: ListView(
  174. children: spots!.map((i) {
  175. return ListTile(
  176. title: Text(i.getName()!),
  177. subtitle: Text(
  178. "${I18N.of("消耗热量")}: ${i.getHkCalorie()} (h/kcal)"),
  179. trailing: Text("${I18N.of("运动次数")}: ${i.getSportTimes()}"),
  180. onTap: () {
  181. onSelected(i);
  182. },
  183. onLongPress: () async {
  184. await PopMenus.sliderConfirm(
  185. context: context,
  186. content: Text(I18N.of("滑动来删除该条数据")),
  187. function: () async {
  188. if (i.getSportTimes() == 0) {
  189. await SportInfoMapper().delete(i);
  190. await loadData();
  191. await PopMenus.attention(
  192. context: context,
  193. content: Text(I18N.of("删除成功")));
  194. } else {
  195. await PopMenus.attention(
  196. context: context,
  197. content: Text(I18N.of("您不能删除记录过的运动")));
  198. }
  199. },
  200. );
  201. },
  202. );
  203. }).toList(),
  204. ),
  205. ),
  206. ],
  207. ),
  208. ),
  209. );
  210. }
  211. }
  212. class _SportSearchDelegate extends SearchDelegate<String?> {
  213. List<SportInfo>? spots;
  214. _SportSearchDelegate(this.spots);
  215. @override
  216. Widget buildLeading(BuildContext context) {
  217. return IconButton(
  218. icon: Icon(Icons.arrow_back),
  219. onPressed: () {
  220. this.close(context, null);
  221. },
  222. );
  223. }
  224. @override
  225. List<Widget> buildActions(BuildContext context) {
  226. return [
  227. IconButton(
  228. icon: Icon(Icons.clear),
  229. onPressed: () {
  230. query = "";
  231. showSuggestions(context);
  232. },
  233. ),
  234. ];
  235. }
  236. @override
  237. Widget buildResults(BuildContext context) {
  238. return Padding(
  239. padding: EdgeInsets.all(16),
  240. child: ListView(
  241. children:
  242. spots!.where((test) => test.getName()!.contains(query)).map((i) {
  243. return ListTile(
  244. title: Text(i.getName()!),
  245. subtitle: Text("${I18N.of("消耗热量")}: ${i.getHkCalorie()} (h/kcal)"),
  246. trailing: Text("${I18N.of("运动次数")}: ${i.getSportTimes()}"),
  247. onTap: () {
  248. this.close(context, i.getName());
  249. },
  250. onLongPress: () async {
  251. await PopMenus.sliderConfirm(
  252. context: context,
  253. content: Text(I18N.of("滑动来删除该条数据")),
  254. function: () async {
  255. if (i.getSportTimes() == 0) {
  256. await SportInfoMapper().delete(i);
  257. await PopMenus.attention(
  258. context: context, content: Text(I18N.of("删除成功")));
  259. this.close(context, null);
  260. } else {
  261. await PopMenus.attention(
  262. context: context,
  263. content: Text(I18N.of("您不能删除记录过的运动")));
  264. }
  265. },
  266. );
  267. },
  268. );
  269. }).toList(),
  270. ),
  271. );
  272. }
  273. @override
  274. Widget buildSuggestions(BuildContext context) {
  275. return buildResults(context);
  276. }
  277. @override
  278. ThemeData appBarTheme(BuildContext context) {
  279. return Theme.of(context);
  280. }
  281. @override
  282. String get searchFieldLabel => I18N.of("搜索");
  283. }