This commit is contained in:
DESKTOP-I3JPKHK\wy 2025-09-19 10:42:46 +08:00
parent 223f225d94
commit 3d1729f630
3 changed files with 1113 additions and 827 deletions

336
USAGE_GUIDE.md Normal file
View File

@ -0,0 +1,336 @@
# App Upgrade Plugin - 完整使用指南
## 🚀 快速开始
### 基础用法(一行代码)
```dart
import 'package:app_upgrade_plugin/app_upgrade_plugin.dart';
// 最简单的使用方式
await AppUpgradeSimple.instance.checkUpdate(
context: context,
url: 'https://api.example.com/check-update',
);
```
## ⚙️ 配置选项
### 预设配置
```dart
// 自动更新模式
AppUpgradeSimple.instance.configure(UpgradeConfig.auto);
// 静默检查模式
AppUpgradeSimple.instance.configure(UpgradeConfig.silent);
// 开发模式
AppUpgradeSimple.instance.configure(UpgradeConfig.development);
// 生产模式
AppUpgradeSimple.instance.configure(UpgradeConfig.production);
```
### 自定义配置
```dart
AppUpgradeSimple.instance.configure(UpgradeConfig(
showNoUpdateToast: true, // 显示无更新提示
autoDownload: false, // 自动下载
autoInstall: false, // 自动安装
connectionTimeout: 30, // 连接超时(秒)
downloadTimeout: 300, // 下载超时(秒)
installTimeout: 45, // 安装检测超时(秒)
enableDebugLog: true, // 启用调试日志
customToast: (message) { // 自定义Toast
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
},
));
```
## 🎯 使用场景
### 1. 应用启动时检查更新
```dart
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (context) {
// 应用启动后检查更新
WidgetsBinding.instance.addPostFrameCallback((_) {
AppUpgradeSimple.instance.checkUpdate(
context: context,
url: 'https://api.example.com/check-update',
);
});
return MyHomePage();
},
),
);
}
}
```
### 2. 定期检查更新
```dart
class MyHomePage extends StatefulWidget {
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Timer? _updateTimer;
@override
void initState() {
super.initState();
// 每24小时检查一次更新
_updateTimer = Timer.periodic(const Duration(hours: 24), (_) {
AppUpgradeSimple.instance.checkUpdateSilent(
url: 'https://api.example.com/check-update',
);
});
}
@override
void dispose() {
_updateTimer?.cancel();
super.dispose();
}
}
```
### 3. 手动检查更新
```dart
ElevatedButton(
onPressed: () async {
await AppUpgradeSimple.instance.checkUpdate(
context: context,
url: 'https://api.example.com/check-update',
params: {
'userId': currentUserId,
'channel': releaseChannel,
},
onComplete: () {
print('更新检查完成');
},
);
},
child: const Text('检查更新'),
)
```
## 🔧 高级功能
### 预下载APK
```dart
// 后台预下载不显示UI
final filePath = await AppUpgradeSimple.instance.preDownloadApk(
url: 'https://example.com/app.apk',
onProgress: (progress) {
print('下载进度: ${progress.percentage}%');
},
);
```
### 查找已下载的APK
```dart
final existingApk = await AppUpgradeSimple.instance.findDownloadedApk('1.2.0');
if (existingApk != null) {
print('找到已下载的APK: $existingApk');
}
```
### 网络状态检查
```dart
final hasNetwork = await AppUpgradeSimple.instance.checkNetworkStatus();
if (!hasNetwork) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('网络错误'),
content: const Text('请检查网络连接'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('确定'),
),
],
),
);
}
```
### 清理下载缓存
```dart
// 清理所有下载的APK文件
await AppUpgradeSimple.instance.clearDownloadCache();
```
## 🎨 UI定制
### 自定义Toast
```dart
AppUpgradeSimple.instance.configure(UpgradeConfig(
customToast: (message) {
// 使用SnackBar代替Toast
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.deepPurple,
behavior: SnackBarBehavior.floating,
),
);
},
));
```
### 富文本更新内容
服务器返回的更新内容支持简单的富文本格式:
```
**新功能**
- 添加了[暗黑模式]支持
- 优化了`网络请求`性能
- __修复了已知问题__
**重要更新**
请及时更新到最新版本以获得更好的体验!
```
## 📱 平台支持
### Android
- ✅ APK下载和安装
- ✅ 权限自动处理
- ✅ 多应用商店支持
- ✅ 安装状态智能检测
- ✅ 断点续传
- ✅ 文件校验
### iOS
- ✅ App Store跳转
- ✅ 企业证书分发
- ✅ TestFlight支持
## 🔍 调试和测试
### 启用调试日志
```dart
AppUpgradeSimple.instance.configure(UpgradeConfig.development);
```
### 查看日志输出
```
🔍 检查更新结果: UpgradeInfo(...)
⚡ 应用回到前台,立即检查安装状态
✅ 检测结果: 安装成功
❌ 检测结果: 安装未完成
```
### 测试不同场景
插件内置了测试按钮,可以模拟各种安装场景:
- 模拟安装取消
- 模拟权限拒绝
- 模拟安装超时
## 🛡️ 错误处理
插件提供了完善的错误处理机制:
### 网络错误
- 连接超时
- DNS解析失败
- 服务器错误
- 证书验证失败
### 安装错误
- 权限被拒绝
- APK文件损坏
- 存储空间不足
- 系统版本不兼容
### 自动重试
- 智能检测安装状态
- 多种重试方式
- 用户友好的错误提示
## 📊 最佳实践
### 1. 应用启动检查
```dart
// 推荐:应用启动时静默检查
WidgetsBinding.instance.addPostFrameCallback((_) {
AppUpgradeSimple.instance.checkUpdateSilent(
url: 'https://api.example.com/check-update',
).then((info) {
if (info != null && info.hasUpdate) {
// 发现更新,显示提示
showUpdateNotification(context, info);
}
});
});
```
### 2. 定期检查
```dart
// 推荐:每日检查一次
Timer.periodic(const Duration(days: 1), (_) {
AppUpgradeSimple.instance.checkUpdateSilent(
url: 'https://api.example.com/check-update',
);
});
```
### 3. 用户手动检查
```dart
// 推荐:提供手动检查按钮
ElevatedButton(
onPressed: () {
AppUpgradeSimple.instance.checkUpdate(
context: context,
url: 'https://api.example.com/check-update',
);
},
child: const Text('检查更新'),
)
```
## 🎯 完整示例
查看 `example/lib/main_enhanced.dart` 获取完整的使用示例,包含所有功能的演示。
## 📝 API文档
### 主要方法
#### `checkUpdate()`
检查并处理应用更新的主要方法。
#### `checkUpdateSilent()`
静默检查更新不显示UI。
#### `preDownloadApk()`
后台预下载APK文件。
#### `clearDownloadCache()`
清理下载缓存。
#### `checkNetworkStatus()`
检查网络连接状态。
### 配置类
#### `UpgradeConfig`
升级配置类,控制插件的行为。
#### 预设配置
- `UpgradeConfig.auto` - 自动更新
- `UpgradeConfig.silent` - 静默检查
- `UpgradeConfig.development` - 开发模式
- `UpgradeConfig.production` - 生产模式
---
这个插件现在提供了完整的应用升级解决方案,支持各种使用场景和自定义需求!

File diff suppressed because it is too large Load Diff

View File

@ -37,10 +37,146 @@ class _SimpleAppUpgradePlugin {
Future<Map<String, String>> getAppInfo() {
return AppUpgradePluginPlatform.instance.getAppInfo();
}
///
Future<Map<String, String>> getCurrentAppInfo() async {
try {
return await AppUpgradePluginPlatform.instance.getAppInfo();
} catch (e) {
debugPrint('获取应用信息失败: $e');
return {};
}
}
///
Future<String?> getDownloadPath() {
return AppUpgradePluginPlatform.instance.getDownloadPath();
}
///
Future<bool> isPackageInstalled(String packageName) async {
try {
//
final appInfo = await getAppInfo();
final currentPackage = appInfo['packageName'] ?? '';
debugPrint('检查包安装状态: 当前包名=$currentPackage, 目标包名=$packageName');
//
if (currentPackage == packageName) {
return true; //
}
return false;
} catch (e) {
debugPrint('检查包安装状态失败: $e');
return false;
}
}
///
Future<bool> isVersionUpdated(String targetVersion, int? targetBuildNumber) async {
try {
final appInfo = await getCurrentAppInfo();
final currentVersion = appInfo['version'] ?? '';
final currentBuildNumber = int.tryParse(appInfo['buildNumber'] ?? '0') ?? 0;
debugPrint('版本对比: 当前版本=$currentVersion, 目标版本=$targetVersion');
debugPrint('构建号对比: 当前构建号=$currentBuildNumber, 目标构建号=$targetBuildNumber');
//
if (targetBuildNumber != null && targetBuildNumber > 0) {
return currentBuildNumber >= targetBuildNumber;
}
//
return currentVersion == targetVersion;
} catch (e) {
debugPrint('版本对比失败: $e');
return false;
}
}
}
///
class UpgradeConfig {
///
final bool showNoUpdateToast;
///
final bool autoDownload;
///
final bool autoInstall;
///
final int connectionTimeout;
///
final int downloadTimeout;
///
final int installTimeout;
///
final bool enableDebugLog;
/// Toast显示函数
final void Function(String message)? customToast;
const UpgradeConfig({
this.showNoUpdateToast = true,
this.autoDownload = false,
this.autoInstall = false,
this.connectionTimeout = 30,
this.downloadTimeout = 300,
this.installTimeout = 45,
this.enableDebugLog = true,
this.customToast,
});
///
static const UpgradeConfig auto = UpgradeConfig(
autoDownload: true,
autoInstall: true,
);
///
static const UpgradeConfig silent = UpgradeConfig(
showNoUpdateToast: false,
enableDebugLog: false,
);
/// +
static const UpgradeConfig development = UpgradeConfig(
enableDebugLog: true,
installTimeout: 30,
connectionTimeout: 10,
);
/// +
static const UpgradeConfig production = UpgradeConfig(
showNoUpdateToast: false,
enableDebugLog: false,
installTimeout: 60,
connectionTimeout: 30,
);
}
/// App升级管理器
/// APIApp升级功能
///
/// : 2.0.0
/// : 2025-09-18
///
/// :
/// - 🎨 UI设计Material Design 3
/// - 🔄
/// - 📱
/// - 使
/// - 🎯
/// - 🚀
/// - 🛡
class AppUpgradeSimple {
static AppUpgradeSimple? _instance;
@ -61,10 +197,60 @@ class AppUpgradeSimple {
AppUpgradeSimple._() : _plugin = _SimpleAppUpgradePlugin.instance;
final _SimpleAppUpgradePlugin _plugin;
UpgradeConfig _config = const UpgradeConfig();
///
void configure(UpgradeConfig config) {
_config = config;
}
///
void enableAutoUpdate() {
_config = UpgradeConfig.auto;
}
///
void enableSilentCheck() {
_config = UpgradeConfig.silent;
}
///
Future<bool> checkNetworkStatus() async {
try {
final result = await InternetAddress.lookup('google.com');
return result.isNotEmpty && result[0].rawAddress.isNotEmpty;
} catch (e) {
debugPrint('网络检查失败: $e');
return false;
}
}
///
Future<void> clearDownloadCache() async {
try {
final downloadPath = await _plugin.getDownloadPath();
if (downloadPath != null) {
final dir = Directory(downloadPath);
if (await dir.exists()) {
final files = await dir.list().where((file) => file.path.endsWith('.apk')).toList();
for (final file in files) {
try {
await file.delete();
debugPrint('已删除缓存文件: ${file.path}');
} catch (e) {
debugPrint('删除缓存文件失败: ${file.path}, 错误: $e');
}
}
}
}
} catch (e) {
debugPrint('清理下载缓存失败: $e');
}
}
/// 使
///
///
///
/// ```dart
/// AppUpgradeSimple.instance.checkUpdate(
/// context: context,
@ -72,25 +258,64 @@ class AppUpgradeSimple {
/// );
/// ```
///
///
/// ```dart
/// //
/// AppUpgradeSimple.instance.configure(UpgradeConfig.auto);
///
/// // 使
/// AppUpgradeSimple.instance.configure(UpgradeConfig(
/// autoDownload: true,
/// autoInstall: false,
/// installTimeout: 60,
/// customToast: (message) => ScaffoldMessenger.of(context).showSnackBar(
/// SnackBar(content: Text(message)),
/// ),
/// ));
///
/// //
/// await AppUpgradeSimple.instance.checkUpdate(
/// context: context,
/// url: 'https://api.example.com/check-update',
/// params: {'userId': '123', 'channel': 'official'},
/// onComplete: () => print('更新检查完成'),
/// );
/// ```
///
/// [context] 访 `Navigator` `MaterialLocalizations`
/// `init` `navigatorKey`
/// [url] API接口地址
/// [params]
/// [config]
/// [onComplete]
///
Future<void> checkUpdate({
required BuildContext context,
required String url,
Map<String, dynamic>? params,
bool showNoUpdateToast = true,
bool autoDownload = false,
bool autoInstall = false,
bool? showNoUpdateToast,
bool? autoDownload,
bool? autoInstall,
VoidCallback? onComplete,
UpgradeConfig? config,
}) async {
// 使
final effectiveConfig = config ?? _config;
final finalShowNoUpdateToast = showNoUpdateToast ?? effectiveConfig.showNoUpdateToast;
final finalAutoDownload = autoDownload ?? effectiveConfig.autoDownload;
final finalAutoInstall = autoInstall ?? effectiveConfig.autoInstall;
try {
assert(_canShowMaterialDialog(context), '请在 MaterialApp 环境内调用');
//
final info = await _plugin.checkUpdate(url, params: params);
print('info: $info');
if (effectiveConfig.enableDebugLog) {
debugPrint('🔍 检查更新结果: $info');
}
if (info == null || !info.hasUpdate) {
if (showNoUpdateToast && context.mounted) _showToast('已是最新版本');
if (finalShowNoUpdateToast && context.mounted) {
_showToast('已是最新版本', effectiveConfig);
}
onComplete?.call();
return;
}
@ -98,9 +323,10 @@ class AppUpgradeSimple {
await _showUpgradeDialog(
context: context,
info: info,
autoDownload: autoDownload,
autoInstall: autoInstall,
autoDownload: finalAutoDownload,
autoInstall: finalAutoInstall,
onComplete: onComplete,
config: effectiveConfig,
);
} catch (e) {
debugPrint('检查更新失败: $e');
@ -121,11 +347,13 @@ class AppUpgradeSimple {
}
if (context.mounted) {
_showToast(errorMessage);
_showToast(errorMessage, effectiveConfig);
//
if (errorString.contains('Failed host lookup') || errorString.contains('无网络连接')) {
debugPrint('建议: 请检查网络连接或尝试使用网络诊断功能');
if (effectiveConfig.enableDebugLog) {
debugPrint('💡 建议: 请检查网络连接或尝试使用网络诊断功能');
}
}
}
onComplete?.call();
@ -140,11 +368,60 @@ class AppUpgradeSimple {
try {
return await _plugin.checkUpdate(url, params: params);
} catch (e) {
debugPrint('静默检查更新失败: $e');
if (_config.enableDebugLog) {
debugPrint('静默检查更新失败: $e');
}
return null;
}
}
/// APKUI
Future<String?> preDownloadApk({
required String url,
Function(DownloadProgress)? onProgress,
}) async {
try {
return await _plugin.downloadApk(url, onProgress: onProgress);
} catch (e) {
if (_config.enableDebugLog) {
debugPrint('预下载APK失败: $e');
}
return null;
}
}
/// APK文件
Future<String?> findDownloadedApk(String version) async {
try {
final downloadPath = await _plugin.getDownloadPath();
if (downloadPath != null) {
final dir = Directory(downloadPath);
if (await dir.exists()) {
final files = await dir.list().toList();
for (final file in files) {
if (file.path.contains(version) && file.path.endsWith('.apk')) {
final fileEntity = File(file.path);
if (await fileEntity.exists()) {
return file.path;
}
}
}
}
}
return null;
} catch (e) {
if (_config.enableDebugLog) {
debugPrint('查找已下载APK失败: $e');
}
return null;
}
}
///
Future<Map<String, String>> getAppInfo() async {
return await _plugin.getAppInfo();
}
///
Future<void> _showUpgradeDialog({
required BuildContext context,
@ -152,20 +429,27 @@ class AppUpgradeSimple {
required bool autoDownload,
required bool autoInstall,
VoidCallback? onComplete,
UpgradeConfig? config,
}) {
final effectiveConfig = config ?? _config;
return showDialog(
context: context,
barrierDismissible: !info.isForceUpdate,
builder: (context) {
if (info.isForceUpdate) {
return _ForceUpgradeDialog(info: info);
return _ForceUpgradeDialog(
info: info,
config: effectiveConfig,
);
} else {
return _SimpleUpgradeDialog(
info: info,
autoDownload: autoDownload,
autoInstall: autoInstall,
onComplete: onComplete,
showToast: (message) => _showToast(message),
config: effectiveConfig,
showToast: (message) => _showToast(message, effectiveConfig),
);
}
},
@ -173,8 +457,20 @@ class AppUpgradeSimple {
}
/// Toast提示
void _showToast(String message) {
Fluttertoast.showToast(msg: message);
void _showToast(String message, [UpgradeConfig? config]) {
final effectiveConfig = config ?? _config;
if (effectiveConfig.customToast != null) {
effectiveConfig.customToast!(message);
} else {
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.black87,
textColor: Colors.white,
fontSize: 14.0,
);
}
}
/// Material
@ -206,6 +502,7 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
VoidCallback? get onComplete;
bool get autoDownload;
bool get autoInstall;
UpgradeConfig get config;
void initUpgradeLogic() {
if (autoDownload && Platform.isAndroid) {
@ -222,13 +519,14 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
}
void onAppLifecycleStateChanged(AppLifecycleState state) {
debugPrint('应用生命周期状态变化: $state, _isWaitingForInstallation=$_isWaitingForInstallation');
debugPrint('🔄 应用生命周期状态变化: $state, _isWaitingForInstallation=$_isWaitingForInstallation');
//
if (state == AppLifecycleState.resumed && _isWaitingForInstallation) {
debugPrint('应用回到前台,准备检查安装状态');
//
Future.delayed(const Duration(milliseconds: 500), () {
//
if (_isWaitingForInstallation && state == AppLifecycleState.resumed) {
debugPrint('⚡ 应用回到前台,检查安装状态');
//
Future.delayed(const Duration(milliseconds: 1500), () {
if (mounted && _isWaitingForInstallation) {
_checkInstallationResult();
}
@ -321,7 +619,7 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
_isWaitingForInstallation = true;
_statusText = '请完成安装';
});
showToast('请在系统弹窗中完成安装,如取消可点击下方按钮重试');
showToast('请在系统弹窗中完成安装,完成后点击下方确认按钮');
//
_startInstallationTimeoutCheck();
@ -345,80 +643,98 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
}
}
///
///
void _startInstallationTimeoutCheck() {
// 5
Timer(const Duration(seconds: 5), () {
if (mounted && _isWaitingForInstallation && _statusText == '请完成安装') {
debugPrint('5秒后自动检测安装状态');
_checkInstallationResult();
}
});
debugPrint('🚀 启动简化安装检测系统');
// 30
_installCheckTimer = Timer(const Duration(seconds: 30), () {
if (mounted && _isWaitingForInstallation && _statusText == '请完成安装') {
//
//
_installCheckTimer = Timer(Duration(seconds: config.installTimeout), () {
if (mounted && _isWaitingForInstallation) {
debugPrint('⏰ 安装检测超时');
setState(() {
_isWaitingForInstallation = false;
_statusText = '安装超时';
});
showToast('安装检测超时,请检查是否已安装成功');
showToast('安装超时,请使用重试按钮重新安装');
}
});
}
///
///
Future<void> _checkInstallationResult() async {
if (!mounted || !_isWaitingForInstallation) {
debugPrint('跳过安装结果检查: mounted=$mounted, _isWaitingForInstallation=$_isWaitingForInstallation');
return;
}
debugPrint('开始检查安装结果...');
debugPrint('🔍 开始简化安装检测...');
try {
//
final appInfo = await _plugin.getAppInfo();
final currentVersion = appInfo['versionName'] ?? '';
final appInfo = await _plugin.getCurrentAppInfo();
final currentVersion = appInfo['version'] ?? '';
final currentBuildNumber = int.tryParse(appInfo['buildNumber'] ?? '0') ?? 0;
debugPrint('检查安装结果: 当前版本=$currentVersion, 目标版本=${info.versionName}');
debugPrint('📱 当前版本: $currentVersion, 构建号: $currentBuildNumber');
debugPrint('🎯 目标版本: ${info.versionName}, 构建号: ${info.versionBuildNumber}');
//
if (currentVersion == info.versionName) {
debugPrint('检测到版本已更新,安装成功');
_installCheckTimer?.cancel();
setState(() {
_isWaitingForInstallation = false;
_statusText = '安装成功';
});
showToast('应用更新成功!');
//
Future.delayed(const Duration(seconds: 1), () {
if (mounted && Navigator.canPop(context)) {
Navigator.of(context).pop();
}
onComplete?.call();
});
//
bool isUpdated = false;
if (info.versionBuildNumber > 0) {
isUpdated = currentBuildNumber >= info.versionBuildNumber;
debugPrint('📊 构建号比较: $currentBuildNumber >= ${info.versionBuildNumber} = $isUpdated');
} else {
//
debugPrint('版本号未更新,用户可能取消了安装');
setState(() {
_isWaitingForInstallation = false;
_statusText = '安装被取消';
});
showToast('安装被取消,可以重新尝试安装');
//
isUpdated = currentVersion == info.versionName;
debugPrint('📊 版本号比较: $currentVersion == ${info.versionName} = $isUpdated');
}
if (isUpdated) {
debugPrint('✅ 检测结果: 安装成功');
_handleInstallationSuccess();
} else {
debugPrint('❌ 检测结果: 安装被取消(版本未更新)');
_handleInstallationCancelled();
}
} catch (e) {
debugPrint('检测应用状态失败: $e');
debugPrint('检测失败: $e');
setState(() {
_isWaitingForInstallation = false;
_statusText = '检测失败';
});
showToast('无法检测安装状态,请手动检查');
showToast('无法检测安装状态,请手动确认');
}
}
///
void _handleInstallationSuccess() {
_installCheckTimer?.cancel();
setState(() {
_isWaitingForInstallation = false;
_statusText = '安装成功';
});
showToast('应用更新成功!');
//
Future.delayed(const Duration(seconds: 1), () {
if (mounted && Navigator.canPop(context)) {
Navigator.of(context).pop();
}
onComplete?.call();
});
}
///
void _handleInstallationCancelled() {
setState(() {
_isWaitingForInstallation = false;
_statusText = '安装被取消';
});
showToast('安装被取消,可以点击重试按钮重新安装');
}
/// APK
Future<void> _retryInstall() async {
if (_downloadedFilePath != null) {
@ -842,16 +1158,21 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
//
Row(
children: [
Container(
AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: _getStatusColor(colorScheme).withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(
_getStatusIcon(),
color: _getStatusColor(colorScheme),
size: 20,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: Icon(
_getStatusIcon(),
key: ValueKey(_statusText),
color: _getStatusColor(colorScheme),
size: 20,
),
),
),
const SizedBox(width: 12),
@ -859,12 +1180,16 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_statusText,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: colorScheme.onSurface,
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: Text(
_statusText,
key: ValueKey(_statusText),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: colorScheme.onSurface,
),
),
),
const SizedBox(height: 2),
@ -878,81 +1203,56 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
),
),
//
if (showRetryButton &&
(_statusText == '安装失败' ||
_statusText == '安装异常' ||
_statusText == '权限被拒绝' ||
_statusText == '请完成安装' ||
_statusText == '安装超时' ||
_statusText == '安装被取消' ||
_statusText == '检测失败'))
if (_shouldShowRetryOptions())
Text(
_statusText == '请完成安装' ? '可重新启动安装' : '点击区域重试',
_getClickHintText(),
style: TextStyle(
fontSize: 11,
color: colorScheme.onSurface.withOpacity(0.6),
),
),
// "请完成安装"
// "请完成安装"
if (_statusText == '请完成安装')
Padding(
padding: const EdgeInsets.only(top: 8),
child: Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: _checkInstallationResult,
icon: const Icon(Icons.refresh, size: 14),
label: const Text('检查状态', style: TextStyle(fontSize: 11)),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
backgroundColor: colorScheme.secondary.withOpacity(0.8),
),
),
padding: const EdgeInsets.only(top: 12),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: colorScheme.primaryContainer.withOpacity(0.2),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: colorScheme.primary.withOpacity(0.3),
width: 1,
),
const SizedBox(width: 4),
Expanded(
child: ElevatedButton.icon(
onPressed: () async {
//
if (_downloadedFilePath != null) {
await _installApk(_downloadedFilePath!);
}
},
icon: const Icon(Icons.install_mobile, size: 14),
label: const Text('重新安装', style: TextStyle(fontSize: 11)),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
backgroundColor: colorScheme.primary,
),
),
child: Column(
children: [
Icon(
Icons.touch_app,
color: colorScheme.primary,
size: 24,
),
),
const SizedBox(width: 4),
//
Expanded(
child: ElevatedButton.icon(
onPressed: () {
setState(() {
_isWaitingForInstallation = false;
_statusText = '安装被取消';
});
showToast('已模拟安装被取消,现在应该显示重试按钮');
},
icon: const Icon(Icons.cancel, size: 14),
label: const Text('模拟取消', style: TextStyle(fontSize: 10)),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
backgroundColor: colorScheme.error.withOpacity(0.8),
const SizedBox(height: 8),
Text(
'请在系统安装界面完成操作',
style: TextStyle(
fontSize: 13,
color: colorScheme.onSurface,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
),
],
const SizedBox(height: 4),
Text(
'系统将自动检测安装结果',
style: TextStyle(
fontSize: 11,
color: colorScheme.onSurface.withOpacity(0.7),
),
textAlign: TextAlign.center,
),
],
),
),
),
],
@ -1046,6 +1346,12 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
return Icons.check_circle;
} else if (_statusText == '请完成安装') {
return Icons.touch_app;
} else if (_statusText == '等待安装中') {
return Icons.hourglass_bottom;
} else if (_statusText == '等待确认中') {
return Icons.help_outline;
} else if (_statusText == '等待超时') {
return Icons.timer_off;
} else if (_statusText == '安装被取消') {
return Icons.cancel_outlined;
} else if (_statusText == '安装超时' || _statusText == '检测失败') {
@ -1067,6 +1373,12 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
return colorScheme.tertiary;
} else if (_statusText == '请完成安装') {
return colorScheme.secondary;
} else if (_statusText == '等待安装中') {
return colorScheme.secondary.withOpacity(0.9);
} else if (_statusText == '等待确认中') {
return Colors.orange;
} else if (_statusText == '等待超时') {
return colorScheme.error.withOpacity(0.7);
} else if (_statusText == '安装被取消') {
return colorScheme.secondary.withOpacity(0.8);
} else if (_statusText == '安装超时' || _statusText == '检测失败') {
@ -1088,7 +1400,10 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
_statusText == '安装异常' ||
_statusText == '安装超时' ||
_statusText == '安装被取消' ||
_statusText == '检测失败') {
_statusText == '检测失败' ||
_statusText == '等待安装中' ||
_statusText == '等待确认中' ||
_statusText == '等待超时') {
return Icons.refresh;
} else if (_statusText == '请完成安装') {
return Icons.launch;
@ -1105,8 +1420,11 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
_statusText == '安装异常' ||
_statusText == '安装超时' ||
_statusText == '安装被取消' ||
_statusText == '检测失败') {
_statusText == '检测失败' ||
_statusText == '等待超时') {
return '重试';
} else if (_statusText == '等待安装中' || _statusText == '等待确认中') {
return '重新安装';
} else if (_statusText == '请完成安装') {
return '重新安装';
} else {
@ -1120,7 +1438,7 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
return colorScheme.secondary;
} else if (_statusText == '安装失败' || _statusText == '安装异常') {
return colorScheme.error;
} else if (_statusText == '安装超时' || _statusText == '安装被取消' || _statusText == '检测失败') {
} else if (_statusText == '安装超时' || _statusText == '安装被取消' || _statusText == '检测失败' || _statusText == '等待安装中') {
return colorScheme.secondary.withOpacity(0.8);
} else if (_statusText == '请完成安装') {
return colorScheme.secondary;
@ -1137,6 +1455,9 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
_statusText == '权限被拒绝' ||
_statusText == '安装超时' ||
_statusText == '检测失败' ||
_statusText == '等待安装中' ||
_statusText == '等待确认中' ||
_statusText == '等待超时' ||
(_downloadedFilePath != null &&
!_isDownloading &&
!_isInstalling &&
@ -1145,6 +1466,28 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
_statusText != '安装成功');
}
///
String _getClickHintText() {
switch (_statusText) {
case '请完成安装':
return '完成后请点击确认按钮';
case '等待安装中':
return '点击重新安装';
case '等待确认中':
return '请点击确认按钮';
case '等待超时':
return '点击重新安装';
case '安装被取消':
return '点击重新安装';
case '权限被拒绝':
return '点击打开设置';
case '安装超时':
return '点击重新尝试';
default:
return '点击区域重试';
}
}
void _handleAction() {
if (Platform.isAndroid) {
_handleAndroidAction();
@ -1341,6 +1684,7 @@ class _SimpleUpgradeDialog extends StatefulWidget {
final bool autoInstall;
final VoidCallback? onComplete;
final void Function(String) showToast;
final UpgradeConfig config;
const _SimpleUpgradeDialog({
required this.info,
@ -1348,6 +1692,7 @@ class _SimpleUpgradeDialog extends StatefulWidget {
required this.autoInstall,
this.onComplete,
required this.showToast,
required this.config,
});
@override
@ -1365,6 +1710,8 @@ class _SimpleUpgradeDialogState extends State<_SimpleUpgradeDialog> with _Upgrad
bool get autoDownload => widget.autoDownload;
@override
bool get autoInstall => widget.autoInstall;
@override
UpgradeConfig get config => widget.config;
@override
void initState() {
@ -1477,8 +1824,12 @@ class _SimpleUpgradeDialogState extends State<_SimpleUpgradeDialog> with _Upgrad
///
class _ForceUpgradeDialog extends StatefulWidget {
final UpgradeInfo info;
final UpgradeConfig config;
const _ForceUpgradeDialog({required this.info});
const _ForceUpgradeDialog({
required this.info,
required this.config,
});
@override
State<_ForceUpgradeDialog> createState() => _ForceUpgradeDialogState();
@ -1488,13 +1839,15 @@ class _ForceUpgradeDialogState extends State<_ForceUpgradeDialog> with _UpgradeD
@override
UpgradeInfo get info => widget.info;
@override
void Function(String) get showToast => (message) => AppUpgradeSimple.instance._showToast(message);
void Function(String) get showToast => (message) => AppUpgradeSimple.instance._showToast(message, widget.config);
@override
VoidCallback? get onComplete => null;
@override
bool get autoDownload => false;
@override
bool get autoInstall => true;
@override
UpgradeConfig get config => widget.config;
@override
void initState() {