flutter_integrated_whb/lib/tools/update/update_dialogs.dart

189 lines
5.8 KiB
Dart
Raw Permalink Normal View History

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('取消'),
),
],
),
);
}
}