cage_change_page.dart 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. import 'package:chicken_farm/core/utils/logger.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:chicken_farm/components/vb_app_bar.dart';
  4. import 'package:chicken_farm/core/utils/toast.dart';
  5. import 'package:chicken_farm/core/services/pda/pda_scan_service.dart';
  6. class CageChangePage extends StatefulWidget {
  7. const CageChangePage({super.key});
  8. @override
  9. State<CageChangePage> createState() => _CageChangePageState();
  10. }
  11. class _CageChangePageState extends State<CageChangePage> {
  12. String? _sourceCageId;
  13. String? _targetCageId;
  14. final List<String> _rfids = [];
  15. // 扫描状态
  16. bool _isScanningSource = false;
  17. bool _isScanningTarget = false;
  18. bool _isScanningRfid = false;
  19. int _scanTag = 1;
  20. @override
  21. void initState() {
  22. super.initState();
  23. PdaScanService.openScanHead().then((success) {
  24. if (success) {
  25. logger.d("扫描头已打开");
  26. } else {
  27. logger.d("扫描头打开失败");
  28. }
  29. });
  30. // 初始化监听
  31. PdaScanService.initScanListener(
  32. onScanResult: (result) {
  33. logger.d("扫码结果:$result");
  34. if (_scanTag == 1) {
  35. // 源笼号
  36. setState(() {
  37. _sourceCageId = result;
  38. _isScanningSource = false;
  39. });
  40. } else if (_scanTag == 2) {
  41. // 目标笼号
  42. setState(() {
  43. _targetCageId = result;
  44. _isScanningTarget = false;
  45. });
  46. } else if (_scanTag == 3) {}
  47. // 扫码成功后停止解码(保留扫描头)
  48. PdaScanService.stopSingleScan();
  49. },
  50. onScanError: (error) {
  51. logger.d("扫码错误:$error");
  52. setState(() {
  53. _isScanningSource = false;
  54. });
  55. },
  56. );
  57. }
  58. @override
  59. void dispose() {
  60. // 手动关闭扫描头,立即释放资源
  61. PdaScanService.closeScanHead();
  62. // 释放通道
  63. PdaScanService.dispose();
  64. super.dispose();
  65. }
  66. @override
  67. Widget build(BuildContext context) {
  68. return Scaffold(
  69. appBar: const VberAppBar(title: '换笼管理', showLeftButton: true),
  70. body: Padding(
  71. padding: const EdgeInsets.all(16.0),
  72. child: Column(
  73. crossAxisAlignment: CrossAxisAlignment.start,
  74. children: [
  75. // 源笼号区域
  76. _buildCageSection(
  77. title: '源笼号',
  78. value: _sourceCageId,
  79. isScanning: _isScanningSource,
  80. onScanPressed: _scanSourceCage,
  81. onChangePressed: _handleChangeSourceCage,
  82. ),
  83. const SizedBox(height: 15),
  84. // 目标笼号区域
  85. _buildCageSection(
  86. title: '目标笼号',
  87. value: _targetCageId,
  88. isScanning: _isScanningTarget,
  89. onScanPressed: _scanTargetCage,
  90. onChangePressed: _handleChangeTargetCage,
  91. ),
  92. const SizedBox(height: 15),
  93. // 电子编号区域
  94. _buildRfidSection(),
  95. const SizedBox(height: 15),
  96. // 提交按钮
  97. SizedBox(
  98. width: double.infinity,
  99. child: ElevatedButton(
  100. onPressed:
  101. _sourceCageId != null &&
  102. _targetCageId != null &&
  103. _rfids.isNotEmpty
  104. ? _handleSubmit
  105. : null,
  106. style: ElevatedButton.styleFrom(
  107. backgroundColor:
  108. (_sourceCageId != null &&
  109. _targetCageId != null &&
  110. _rfids.isNotEmpty)
  111. ? Colors.blue
  112. : Colors.grey,
  113. foregroundColor: Colors.white,
  114. ),
  115. child: const Text('提交'),
  116. ),
  117. ),
  118. const SizedBox(height: 20),
  119. // 已扫描的电子编号列表
  120. if (_rfids.isNotEmpty) ...[
  121. const Text(
  122. '已扫描的电子编号',
  123. style: TextStyle(fontWeight: FontWeight.bold),
  124. ),
  125. const SizedBox(height: 10),
  126. Expanded(
  127. child: Container(
  128. padding: const EdgeInsets.all(10),
  129. decoration: BoxDecoration(
  130. border: Border.all(color: Colors.grey),
  131. borderRadius: BorderRadius.circular(8),
  132. ),
  133. child: ListView.builder(
  134. padding: EdgeInsets.zero,
  135. itemCount: _rfids.length,
  136. itemBuilder: (context, index) {
  137. return ListTile(
  138. visualDensity: VisualDensity.compact,
  139. contentPadding: const EdgeInsets.symmetric(
  140. horizontal: 2,
  141. vertical: 0,
  142. ),
  143. title: Text(_rfids[index]),
  144. trailing: IconButton(
  145. icon: const Icon(
  146. Icons.delete,
  147. size: 18,
  148. color: Colors.red,
  149. ),
  150. onPressed: () => _removeRfid(index),
  151. ),
  152. );
  153. },
  154. ),
  155. ),
  156. ),
  157. ],
  158. const SizedBox(height: 20),
  159. ],
  160. ),
  161. ),
  162. );
  163. }
  164. Widget _buildCageSection({
  165. required String title,
  166. String? value,
  167. required bool isScanning,
  168. required VoidCallback onScanPressed,
  169. required VoidCallback onChangePressed,
  170. }) {
  171. return Column(
  172. crossAxisAlignment: CrossAxisAlignment.start,
  173. children: [
  174. Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
  175. const SizedBox(height: 10),
  176. Container(
  177. padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
  178. decoration: BoxDecoration(
  179. border: Border.all(color: Colors.grey),
  180. borderRadius: BorderRadius.circular(8),
  181. ),
  182. child: Row(
  183. children: [
  184. Expanded(
  185. child: Text(
  186. value ?? '未扫描',
  187. style: TextStyle(
  188. color: value != null ? Colors.black : Colors.grey,
  189. fontSize: 16,
  190. ),
  191. ),
  192. ),
  193. if (value != null) ...[
  194. IconButton(
  195. icon: const Icon(Icons.refresh, size: 20),
  196. onPressed: onChangePressed,
  197. ),
  198. ] else ...[
  199. IconButton(
  200. icon: isScanning
  201. ? const SizedBox(
  202. width: 20,
  203. height: 20,
  204. child: CircularProgressIndicator(strokeWidth: 2),
  205. )
  206. : const Icon(Icons.qr_code_scanner, size: 20),
  207. onPressed: onScanPressed,
  208. ),
  209. ],
  210. ],
  211. ),
  212. ),
  213. ],
  214. );
  215. }
  216. Widget _buildRfidSection() {
  217. return Column(
  218. crossAxisAlignment: CrossAxisAlignment.start,
  219. children: [
  220. const Text('鸡数量', style: TextStyle(fontWeight: FontWeight.bold)),
  221. const SizedBox(height: 10),
  222. Container(
  223. padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
  224. decoration: BoxDecoration(
  225. border: Border.all(color: Colors.grey),
  226. borderRadius: BorderRadius.circular(8),
  227. ),
  228. child: Row(
  229. children: [
  230. Expanded(
  231. child: Text(
  232. _rfids.isEmpty ? '未扫描' : '已扫描 ${_rfids.length} 只鸡',
  233. style: TextStyle(
  234. color: _rfids.isNotEmpty ? Colors.black : Colors.grey,
  235. fontSize: 16,
  236. ),
  237. ),
  238. ),
  239. IconButton(
  240. icon: _isScanningRfid
  241. ? const SizedBox(
  242. width: 20,
  243. height: 20,
  244. child: CircularProgressIndicator(strokeWidth: 2),
  245. )
  246. : const Icon(Icons.add, size: 20),
  247. onPressed: _scanRfid,
  248. ),
  249. ],
  250. ),
  251. ),
  252. ],
  253. );
  254. }
  255. // 扫描源笼号
  256. void _scanSourceCage() async {
  257. if (_isScanningSource) return;
  258. setState(() {
  259. _isScanningSource = true;
  260. });
  261. _scanTag = 1;
  262. bool success = await PdaScanService.startScan();
  263. if (!success) {
  264. ToastUtil.error("扫码失败");
  265. }
  266. }
  267. // 扫描目标笼号
  268. void _scanTargetCage() async {
  269. if (_isScanningTarget) return;
  270. setState(() {
  271. _isScanningTarget = true;
  272. });
  273. _scanTag = 2;
  274. bool success = await PdaScanService.startScan();
  275. if (!success) {
  276. ToastUtil.error("扫码失败");
  277. }
  278. }
  279. // 扫描电子编号
  280. void _scanRfid() async {
  281. if (_isScanningRfid) return;
  282. setState(() {
  283. _isScanningRfid = true;
  284. });
  285. try {
  286. // 使用实际的RFID读取功能
  287. final result = "qwe"; //await PdaScanService.readRFId();
  288. if (mounted) {
  289. setState(() {
  290. // 每次添加一个新的电子编号到数组开头(模拟按一次实体按钮扫描一个电子编号)
  291. _rfids.insert(0, result);
  292. _isScanningRfid = false;
  293. });
  294. ToastUtil.success('鸡扫描成功');
  295. }
  296. } catch (e) {
  297. if (mounted) {
  298. setState(() {
  299. _isScanningRfid = false;
  300. });
  301. ToastUtil.error('RFID读取失败: $e');
  302. }
  303. }
  304. }
  305. // 重新扫描源笼号
  306. void _handleChangeSourceCage() {
  307. setState(() {
  308. _sourceCageId = null;
  309. });
  310. }
  311. // 重新扫描目标笼号
  312. void _handleChangeTargetCage() {
  313. setState(() {
  314. _targetCageId = null;
  315. });
  316. }
  317. // 移除指定索引的电子编号
  318. void _removeRfid(int index) {
  319. setState(() {
  320. _rfids.removeAt(index);
  321. });
  322. }
  323. // 提交数据
  324. void _handleSubmit() {
  325. // 在实际应用中,这里会发送数据到服务器
  326. ScaffoldMessenger.of(context).showSnackBar(
  327. const SnackBar(content: Text('换笼操作提交成功'), backgroundColor: Colors.green),
  328. );
  329. // 提交后重置表单
  330. setState(() {
  331. _sourceCageId = null;
  332. _targetCageId = null;
  333. _rfids.clear();
  334. });
  335. }
  336. }