FoodRecordPage.dart 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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/database/entity/FoodInfo.dart';
  5. import 'package:flutter_habit/database/mapper/FoodInfoMapper.dart';
  6. class FoodRecordPage extends StatefulWidget {
  7. @override
  8. _FoodRecordPageState createState() => _FoodRecordPageState();
  9. }
  10. class _FoodRecordPageState extends State<FoodRecordPage> {
  11. List<FoodInfo>? foods;
  12. @override
  13. void initState() {
  14. super.initState();
  15. foods = [];
  16. loadData();
  17. }
  18. Future<void> loadData() async {
  19. foods = await FoodInfoMapper()
  20. .selectAll(orderBy: "eatTimes desc,name asc, gkCalorie asc");
  21. setState(() {});
  22. }
  23. Future<void> onSelected(FoodInfo foodInfo) async {
  24. TextEditingController intakeController = TextEditingController();
  25. TextEditingController moneyController = TextEditingController();
  26. List? res = await PopMenus.baseMenu<List>(
  27. context: context,
  28. title: Text(foodInfo.getName()!),
  29. children: <Widget>[
  30. Divider(),
  31. Text("${I18N.of("食用次数")}: ${foodInfo.getEatTimes()}"),
  32. Text("${I18N.of("食物热量")}: ${foodInfo.getHgkCalorie()} (100g/kcal)"),
  33. TextFormField(
  34. keyboardType: TextInputType.number,
  35. controller: intakeController,
  36. decoration: InputDecoration(
  37. icon: Icon(Icons.restaurant),
  38. labelText: "${I18N.of("进食量")} (g)",
  39. hintText: I18N.of("请输入进食量"),
  40. ),
  41. ),
  42. TextFormField(
  43. keyboardType: TextInputType.number,
  44. controller: moneyController,
  45. decoration: InputDecoration(
  46. icon: Icon(Icons.monetization_on),
  47. labelText: "${I18N.of("花费")}",
  48. hintText: I18N.of("请输入花费"),
  49. ),
  50. ),
  51. TextButton(
  52. child: Text(I18N.of("确定")),
  53. onPressed: () async {
  54. if (intakeController.text.trim().isNotEmpty &&
  55. double.tryParse(intakeController.text.trim()) != null &&
  56. double.tryParse(moneyController.text.trim()) != null) {
  57. Navigator.of(context).pop([
  58. foodInfo.getId(),
  59. double.parse(intakeController.text.trim()) / 100,
  60. foodInfo.getEatTimes(),
  61. double.parse(moneyController.text.trim()),
  62. ]);
  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 != null) {
  73. Navigator.of(context).pop(res);
  74. }
  75. }
  76. @override
  77. Widget build(BuildContext context) {
  78. return Scaffold(
  79. appBar: AppBar(
  80. title: Text(I18N.of("选择食物")),
  81. actions: <Widget>[
  82. IconButton(
  83. icon: Icon(Icons.search),
  84. onPressed: () async {
  85. String? name = await showSearch<String?>(
  86. context: context,
  87. delegate: _FoodSearchDelegate(foods),
  88. );
  89. await loadData();
  90. List<FoodInfo> list =
  91. foods!.where((test) => test.getName() == name).toList();
  92. if (list.isNotEmpty) {
  93. await onSelected(list.last);
  94. }
  95. },
  96. ),
  97. ],
  98. ),
  99. body: Padding(
  100. padding: EdgeInsets.all(16),
  101. child: Column(
  102. children: <Widget>[
  103. ListTile(
  104. leading: Icon(Icons.restaurant),
  105. title: Text(I18N.of("添加食物")),
  106. trailing: Icon(Icons.add),
  107. onTap: () async {
  108. TextEditingController nameController =
  109. TextEditingController();
  110. TextEditingController gkCalorieController =
  111. TextEditingController();
  112. await PopMenus.baseMenu<String>(
  113. context: context,
  114. title: Text(I18N.of("添加食物")),
  115. children: <Widget>[
  116. TextFormField(
  117. controller: nameController,
  118. decoration: InputDecoration(
  119. icon: Icon(Icons.restaurant_menu),
  120. labelText: I18N.of("食物名称"),
  121. hintText: I18N.of("请输入食物名称"),
  122. ),
  123. ),
  124. TextFormField(
  125. keyboardType: TextInputType.number,
  126. controller: gkCalorieController,
  127. decoration: InputDecoration(
  128. icon: Icon(Icons.whatshot),
  129. labelText: "${I18N.of("食物热量")} (100g/kcal)",
  130. hintText: I18N.of("请输入食物热量"),
  131. ),
  132. ),
  133. TextButton(
  134. child: Text(I18N.of("确定")),
  135. onPressed: () async {
  136. if (nameController.text.trim().isNotEmpty &&
  137. gkCalorieController.text.trim().isNotEmpty &&
  138. double.tryParse(gkCalorieController.text.trim()) !=
  139. null) {
  140. FoodInfo foodInfo = FoodInfo();
  141. foodInfo.setName(nameController.text.trim());
  142. foodInfo.setHgkCalorie(
  143. double.parse(gkCalorieController.text.trim()));
  144. foodInfo.setEatTimes(0);
  145. bool isSuccess =
  146. await FoodInfoMapper().insert(foodInfo);
  147. if (isSuccess) {
  148. await loadData();
  149. Navigator.of(context).pop();
  150. } else {
  151. await PopMenus.attention(
  152. context: context,
  153. content: Text(I18N.of("该食物已存在")));
  154. }
  155. } else {
  156. await PopMenus.attention(
  157. context: context, content: Text(I18N.of("输入有误")));
  158. }
  159. },
  160. ),
  161. ],
  162. contentPadding: EdgeInsets.all(16),
  163. );
  164. },
  165. ),
  166. Divider(),
  167. Text(
  168. I18N.of("长按可删除食物"),
  169. style: Theme.of(context).textTheme.bodySmall,
  170. ),
  171. Expanded(
  172. child: ListView(
  173. children: foods!.map((i) {
  174. return ListTile(
  175. title: Text(i.getName()!),
  176. subtitle: Text(
  177. "${I18N.of("食物热量")}: ${i.getHgkCalorie()} (100g/kcal)"),
  178. trailing: Text("${I18N.of("食用次数")}: ${i.getEatTimes()}"),
  179. onTap: () {
  180. onSelected(i);
  181. },
  182. onLongPress: () async {
  183. await PopMenus.sliderConfirm(
  184. context: context,
  185. content: Text(I18N.of("滑动来删除该条数据")),
  186. function: () async {
  187. if (i.getEatTimes() == 0) {
  188. await FoodInfoMapper().delete(i);
  189. await loadData();
  190. await PopMenus.attention(
  191. context: context,
  192. content: Text(I18N.of("删除成功")));
  193. } else {
  194. await PopMenus.attention(
  195. context: context,
  196. content: Text(I18N.of("您不能删除吃过的食物")));
  197. }
  198. },
  199. );
  200. },
  201. );
  202. }).toList(),
  203. ),
  204. ),
  205. ],
  206. ),
  207. ),
  208. );
  209. }
  210. }
  211. class _FoodSearchDelegate extends SearchDelegate<String?> {
  212. List<FoodInfo>? foods;
  213. _FoodSearchDelegate(this.foods);
  214. @override
  215. Widget buildLeading(BuildContext context) {
  216. return IconButton(
  217. icon: Icon(Icons.arrow_back),
  218. onPressed: () {
  219. this.close(context, null);
  220. },
  221. );
  222. }
  223. @override
  224. List<Widget> buildActions(BuildContext context) {
  225. return [
  226. IconButton(
  227. icon: Icon(Icons.clear),
  228. onPressed: () {
  229. query = "";
  230. showSuggestions(context);
  231. },
  232. ),
  233. ];
  234. }
  235. @override
  236. Widget buildResults(BuildContext context) {
  237. return Padding(
  238. padding: EdgeInsets.all(16),
  239. child: ListView(
  240. children:
  241. foods!.where((test) => test.getName()!.contains(query)).map((i) {
  242. return ListTile(
  243. title: Text(i.getName()!),
  244. subtitle:
  245. Text("${I18N.of("食物热量")}: ${i.getHgkCalorie()} (100g/kcal)"),
  246. trailing: Text("${I18N.of("食用次数")}: ${i.getEatTimes()}"),
  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.getEatTimes() == 0) {
  256. await FoodInfoMapper().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, content: Text(I18N.of("您不能删除吃过的食物")));
  263. }
  264. },
  265. );
  266. },
  267. );
  268. }).toList(),
  269. ),
  270. );
  271. }
  272. @override
  273. Widget buildSuggestions(BuildContext context) {
  274. return buildResults(context);
  275. }
  276. @override
  277. ThemeData appBarTheme(BuildContext context) {
  278. return Theme.of(context);
  279. }
  280. @override
  281. String get searchFieldLabel => I18N.of("搜索");
  282. }