upload_page.dart 9.2 KB

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