upload_page.dart 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import 'package:chicken_farm/core/services/offline_storage_service.dart';
  2. import 'package:chicken_farm/core/services/upload_service.dart';
  3. import 'package:chicken_farm/core/utils/logger.dart';
  4. import 'package:chicken_farm/core/utils/service_checker.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:go_router/go_router.dart';
  7. class UploadPage extends StatefulWidget {
  8. const UploadPage({super.key});
  9. @override
  10. State<UploadPage> createState() => _UploadPageState();
  11. }
  12. // 定义状态常量
  13. class UploadStatus {
  14. static const int preparing = 0; // 准备上传
  15. static const int uploading = 1; // 正在上传
  16. static const int completed = 2; // 上传完成
  17. static const int error = 3; // 上传出错
  18. static const int exception = 4; // 上传异常
  19. }
  20. class _UploadPageState extends State<UploadPage> with WidgetsBindingObserver {
  21. late UploadService _uploadService;
  22. int _totalOperations = 0;
  23. int _uploadedOperations = 0;
  24. int _status = UploadStatus.preparing; // 改为int类型
  25. bool _isUploading = false;
  26. bool _uploadCompleted = false;
  27. @override
  28. void initState() {
  29. super.initState();
  30. _uploadService = UploadService();
  31. logger.d('UploadPage initState');
  32. WidgetsBinding.instance.addObserver(this);
  33. // 初始化时就开始上传
  34. WidgetsBinding.instance.addPostFrameCallback((_) {
  35. _startUpload();
  36. });
  37. }
  38. @override
  39. void dispose() {
  40. WidgetsBinding.instance.removeObserver(this);
  41. super.dispose();
  42. }
  43. @override
  44. void didChangeAppLifecycleState(AppLifecycleState state) async {
  45. super.didChangeAppLifecycleState(state);
  46. //
  47. final pendingOps = await OfflineStorageService().getPendingOperations();
  48. final pendingCount = pendingOps.length;
  49. if (pendingCount > 0) {
  50. logger.d('有$pendingCount个待上传数据');
  51. _registerUploadCallbacks();
  52. }
  53. }
  54. // 根据状态获取对应的文本
  55. String _getStatusText() {
  56. switch (_status) {
  57. case UploadStatus.preparing:
  58. return '准备上传...';
  59. case UploadStatus.uploading:
  60. return '数据上传中...';
  61. case UploadStatus.completed:
  62. return '上传完成';
  63. case UploadStatus.error:
  64. return '上传出错,请稍后再试';
  65. case UploadStatus.exception:
  66. return '上传异常,请稍后再试';
  67. default:
  68. return '未知状态';
  69. }
  70. }
  71. // 根据状态获取对应的文本样式
  72. TextStyle _getStatusTextStyle(BuildContext context) {
  73. switch (_status) {
  74. case UploadStatus.completed:
  75. return Theme.of(
  76. context,
  77. ).textTheme.headlineSmall!.copyWith(color: Colors.green);
  78. case UploadStatus.error:
  79. case UploadStatus.exception:
  80. return Theme.of(
  81. context,
  82. ).textTheme.headlineSmall!.copyWith(color: Colors.red);
  83. default:
  84. return Theme.of(context).textTheme.headlineSmall!;
  85. }
  86. }
  87. Future<void> _startUpload() async {
  88. logger.d('准备上传');
  89. // 检查网络
  90. if (!await ServiceChecker().checkService()) {
  91. setState(() {
  92. _isUploading = false;
  93. _status = UploadStatus.error; // 使用状态常量
  94. });
  95. return;
  96. }
  97. // 开始上传
  98. try {
  99. _uploadService.showUpload();
  100. _registerUploadCallbacks();
  101. await _uploadService.startUpload();
  102. } catch (e) {
  103. logger.e(e);
  104. setState(() {
  105. _isUploading = false;
  106. _status = UploadStatus.exception; // 使用状态常量
  107. });
  108. }
  109. }
  110. void _registerUploadCallbacks() {
  111. _uploadService.showUpload();
  112. setState(() {
  113. _isUploading = true;
  114. _status = UploadStatus.uploading; // 使用状态常量
  115. _uploadedOperations = 0;
  116. _totalOperations = 0;
  117. _uploadCompleted = false;
  118. });
  119. int t = DateTime.now().millisecondsSinceEpoch;
  120. logger.d('开始上传,$t');
  121. // 注册回调
  122. _uploadService.registerCallbacks(
  123. onProgress: (uploaded, total, status) {
  124. if (mounted) {
  125. setState(() {
  126. _uploadedOperations = uploaded;
  127. _totalOperations = total;
  128. // 进度更新时不改变状态码,保持uploading状态
  129. });
  130. }
  131. logger.d('上传进度 $t:$uploaded/$total');
  132. },
  133. onComplete: () {
  134. if (mounted) {
  135. setState(() {
  136. _isUploading = false;
  137. _uploadCompleted = true;
  138. _status = UploadStatus.completed; // 使用状态常量
  139. });
  140. }
  141. logger.d('上传完成');
  142. },
  143. onError: (message) {
  144. logger.e('上传出错:$message');
  145. if (mounted) {
  146. setState(() {
  147. _isUploading = false;
  148. _status = UploadStatus.error; // 使用状态常量
  149. });
  150. }
  151. },
  152. );
  153. }
  154. @override
  155. Widget build(BuildContext context) {
  156. return PopScope(
  157. canPop: false, // 禁用返回键
  158. onPopInvokedWithResult: (bool didPop, Object? result) {
  159. // 返回键被触发时的回调,但因为 canPop 为 false,所以不会真正返回
  160. },
  161. child: Scaffold(
  162. appBar: AppBar(
  163. title: const Text('数据上传'),
  164. centerTitle: true,
  165. automaticallyImplyLeading: false, // 禁用默认的返回图标
  166. ),
  167. body: LayoutBuilder(
  168. builder: (context, constraints) {
  169. return SingleChildScrollView(
  170. child: ConstrainedBox(
  171. constraints: BoxConstraints(minHeight: constraints.maxHeight),
  172. child: IntrinsicHeight(
  173. child: Center(
  174. child: Padding(
  175. padding: const EdgeInsets.all(16.0),
  176. child: Column(
  177. mainAxisAlignment: MainAxisAlignment.center,
  178. crossAxisAlignment: CrossAxisAlignment.center,
  179. children: [
  180. Text(
  181. _getStatusText(), // 使用方法获取状态文本
  182. style: _getStatusTextStyle(context), // 使用方法获取样式
  183. textAlign: TextAlign.center,
  184. ),
  185. const SizedBox(height: 30),
  186. if (_totalOperations > 0) ...[
  187. LinearProgressIndicator(
  188. value: _uploadedOperations / _totalOperations,
  189. minHeight: 10,
  190. ),
  191. const SizedBox(height: 10),
  192. Text('$_uploadedOperations / $_totalOperations'),
  193. const SizedBox(height: 30),
  194. ],
  195. if (_isUploading) ...[
  196. const SizedBox(
  197. width: 50,
  198. height: 50,
  199. child: CircularProgressIndicator(),
  200. ),
  201. const SizedBox(height: 20),
  202. SizedBox(
  203. width: 120,
  204. child: ElevatedButton(
  205. onPressed: () {
  206. _uploadService.hideUpload();
  207. context.pop(2); // 后台上传
  208. },
  209. child: const Text('后台上传'),
  210. ),
  211. ),
  212. ] else if (_uploadCompleted) ...[
  213. SizedBox(
  214. width: 120,
  215. child: ElevatedButton(
  216. onPressed: () {
  217. context.pop(1); // 上传成功
  218. },
  219. child: const Text('返回'),
  220. ),
  221. ),
  222. ] else ...[
  223. SizedBox(
  224. width: 120,
  225. child: ElevatedButton(
  226. onPressed: () {
  227. context.pop(0); // 取消上传
  228. },
  229. child: const Text('退出'),
  230. ),
  231. ),
  232. ],
  233. ],
  234. ),
  235. ),
  236. ),
  237. ),
  238. ),
  239. );
  240. },
  241. ),
  242. ),
  243. );
  244. }
  245. }