upload_page.dart 9.1 KB

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