189 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Dart
		
	
	
		
		
			
		
	
	
			189 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Dart
		
	
	
|  | import 'dart:async'; | ||
|  | import 'package:flutter/material.dart'; | ||
|  | import 'package:qhd_prevention/services/update_service.dart'; | ||
|  | 
 | ||
|  | /// 显示“发现新版本”确认对话框,点击更新后会弹出下载进度弹窗
 | ||
|  | Future<void> showUpdateConfirmDialog(BuildContext context, { | ||
|  |   required String apkUrl, | ||
|  |   String title = '发现新版本', | ||
|  |   String content = '检测到新版本,是否立即更新?', | ||
|  |   String updateButtonText = '更新', | ||
|  |   String cancelButtonText = '稍后', | ||
|  | }) async { | ||
|  |   final confirmed = await showDialog<bool>( | ||
|  |     context: context, | ||
|  |     barrierDismissible: false, | ||
|  |     builder: (ctx) { | ||
|  |       return AlertDialog( | ||
|  |         title: Text(title), | ||
|  |         content: Text(content), | ||
|  |         actions: [ | ||
|  |           TextButton( | ||
|  |             onPressed: () => Navigator.of(ctx).pop(false), | ||
|  |             child: Text(cancelButtonText), | ||
|  |           ), | ||
|  |           ElevatedButton( | ||
|  |             onPressed: () => Navigator.of(ctx).pop(true), | ||
|  |             child: Text(updateButtonText), | ||
|  |           ), | ||
|  |         ], | ||
|  |       ); | ||
|  |     }, | ||
|  |   ); | ||
|  | 
 | ||
|  |   if (confirmed == true) { | ||
|  |     // 跳出下载进度弹窗(阻塞式,直到弹窗被关闭)
 | ||
|  |     await showDialog( | ||
|  |       context: context, | ||
|  |       barrierDismissible: false, | ||
|  |       builder: (ctx) => DownloadProgressDialog(apkUrl: apkUrl), | ||
|  |     ); | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | /// 下载进度弹窗(会在 initState 里自动开始下载)
 | ||
|  | class DownloadProgressDialog extends StatefulWidget { | ||
|  |   final String apkUrl; | ||
|  |   final String? apkFileName; | ||
|  |   const DownloadProgressDialog({Key? key, required this.apkUrl, this.apkFileName}) : super(key: key); | ||
|  | 
 | ||
|  |   @override | ||
|  |   State<DownloadProgressDialog> createState() => _DownloadProgressDialogState(); | ||
|  | } | ||
|  | 
 | ||
|  | class _DownloadProgressDialogState extends State<DownloadProgressDialog> { | ||
|  |   final UpdateService _service = UpdateService(); | ||
|  |   StreamSubscription<UpdateEvent>? _sub; | ||
|  |   double _progress = 0.0; | ||
|  |   String _statusText = '准备下载...'; | ||
|  |   bool _isWorking = true; // 下载或安装过程中禁止重复操作
 | ||
|  | 
 | ||
|  |   @override | ||
|  |   void initState() { | ||
|  |     super.initState(); | ||
|  | 
 | ||
|  |     // 监听状态流
 | ||
|  |     _sub = _service.statusStream.listen((event) { | ||
|  |       // 根据事件更新 UI
 | ||
|  |       setState(() { | ||
|  |         switch (event.state) { | ||
|  |           case UpdateState.idle: | ||
|  |             _statusText = '空闲'; | ||
|  |             break; | ||
|  |           case UpdateState.starting: | ||
|  |             _statusText = '准备中...'; | ||
|  |             break; | ||
|  |           case UpdateState.downloading: | ||
|  |             _statusText = '下载中...'; | ||
|  |             break; | ||
|  |           case UpdateState.completed: | ||
|  |             _statusText = '下载完成,正在准备安装...'; | ||
|  |             break; | ||
|  |           case UpdateState.installing: | ||
|  |             _statusText = '发起安装...'; | ||
|  |             break; | ||
|  |           case UpdateState.installed: | ||
|  |             _statusText = '已触发安装'; | ||
|  |             break; | ||
|  |           case UpdateState.failed: | ||
|  |             _statusText = '失败: ${event.message ?? ''}'; | ||
|  |             break; | ||
|  |           case UpdateState.canceled: | ||
|  |             _statusText = '已取消'; | ||
|  |             break; | ||
|  |         } | ||
|  |       }); | ||
|  | 
 | ||
|  |       // 根据不同状态决定是否关闭弹窗或提示
 | ||
|  |       if (event.state == UpdateState.installing || event.state == UpdateState.installed) { | ||
|  |         // 已发起安装,关闭弹窗让系统安装界面出现
 | ||
|  |         if (mounted) Future.delayed(const Duration(milliseconds: 300), () { | ||
|  |           if (mounted) Navigator.of(context).pop(); | ||
|  |         }); | ||
|  |       } else if (event.state == UpdateState.failed) { | ||
|  |         // 显示错误并在短时间后自动关闭弹窗
 | ||
|  |         _isWorking = false; | ||
|  |         Future.delayed(const Duration(milliseconds: 800), () { | ||
|  |           if (mounted) Navigator.of(context).pop(); | ||
|  |           if (mounted) ScaffoldMessenger.of(context).showSnackBar( | ||
|  |             SnackBar(content: Text('更新失败:${event.message ?? '未知错误'}')), | ||
|  |           ); | ||
|  |         }); | ||
|  |       } else if (event.state == UpdateState.canceled) { | ||
|  |         _isWorking = false; | ||
|  |         if (mounted) { | ||
|  |           Future.delayed(const Duration(milliseconds: 200), () { | ||
|  |             if (mounted) Navigator.of(context).pop(); | ||
|  |             if (mounted) ScaffoldMessenger.of(context).showSnackBar( | ||
|  |               const SnackBar(content: Text('下载已取消')), | ||
|  |             ); | ||
|  |           }); | ||
|  |         } | ||
|  |       } | ||
|  |     }); | ||
|  | 
 | ||
|  |     // 监听进度 ValueNotifier
 | ||
|  |     _service.progress.addListener(_onProgressChanged); | ||
|  | 
 | ||
|  |     // 启动下载
 | ||
|  |     _service.downloadAndInstall(apkUrl: widget.apkUrl, apkFileName: widget.apkFileName); | ||
|  |   } | ||
|  | 
 | ||
|  |   void _onProgressChanged() { | ||
|  |     setState(() { | ||
|  |       _progress = _service.progress.value; | ||
|  |     }); | ||
|  |   } | ||
|  | 
 | ||
|  |   @override | ||
|  |   void dispose() { | ||
|  |     _sub?.cancel(); | ||
|  |     _service.progress.removeListener(_onProgressChanged); | ||
|  |     super.dispose(); | ||
|  |   } | ||
|  | 
 | ||
|  |   void _onCancel() { | ||
|  |     if (!_isWorking) { | ||
|  |       // 如果已经不是工作中,直接关闭
 | ||
|  |       if (Navigator.of(context).canPop()) Navigator.of(context).pop(); | ||
|  |       return; | ||
|  |     } | ||
|  | 
 | ||
|  |     // 取消下载
 | ||
|  |     _service.cancelDownload(); | ||
|  |     // 标记为非工作中(后续状态回调会关闭并提示)
 | ||
|  |     _isWorking = false; | ||
|  |   } | ||
|  | 
 | ||
|  |   @override | ||
|  |   Widget build(BuildContext context) { | ||
|  |     final percent = (_progress * 100).clamp(0.0, 100.0); | ||
|  |     return WillPopScope( | ||
|  |       onWillPop: () async => false, // 禁止物理返回
 | ||
|  |       child: AlertDialog( | ||
|  |         title: const Text('正在更新'), | ||
|  |         content: Column( | ||
|  |           mainAxisSize: MainAxisSize.min, | ||
|  |           children: [ | ||
|  |             LinearProgressIndicator(value: _progress), | ||
|  |             const SizedBox(height: 12), | ||
|  |             Row( | ||
|  |               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
|  |               children: [ | ||
|  |                 Text('${percent.toStringAsFixed(1)}%'), | ||
|  |                 Flexible(child: Text(_statusText, overflow: TextOverflow.ellipsis, textAlign: TextAlign.right)), | ||
|  |               ], | ||
|  |             ), | ||
|  |           ], | ||
|  |         ), | ||
|  |         actions: [ | ||
|  |           TextButton( | ||
|  |             onPressed: _onCancel, | ||
|  |             child: const Text('取消'), | ||
|  |           ), | ||
|  |         ], | ||
|  |       ), | ||
|  |     ); | ||
|  |   } | ||
|  | } |