集成功能 暂停 取消功能 恢复功能的接口的领域层和数据层的开发 下一步待集成到页面上 优化功能,优化了接口异常和未知异常对页面渲染的影响对用户的体验的不好情况。具体通过弹窗友好提示! 优化更新了tcp指示灯点击出现机器状态中添加选中设备的编号 方便用户使用的明白明了!。 优化更新关闭了tcp重连操作内链条中的获取用户设备和切换设备操作项。 调整了获取无人机状态信息的更新频率 为14秒一次
373 lines
14 KiB
Dart
373 lines
14 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter/services.dart';
|
||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||
import 'update_cubit.dart';
|
||
import 'update_state.dart';
|
||
import 'version_check_service.dart';
|
||
|
||
/// 更新对话框(带完整流程提示)
|
||
class UpdateDialog extends StatelessWidget {
|
||
final AppVersionInfo versionInfo;
|
||
|
||
const UpdateDialog({super.key, required this.versionInfo});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return BlocListener<UpdateCubit, UpdateState>(
|
||
listener: (context, state) {
|
||
if (state is UpdateSuccess) {
|
||
Navigator.of(context).pop();
|
||
|
||
if (versionInfo.updateType == 'patch') {
|
||
_showRestartDialog(context);
|
||
}
|
||
} else if (state is UpdateFailure) {
|
||
Navigator.of(context).pop();
|
||
|
||
String errorMessage = state.error;
|
||
bool showApkPath = false;
|
||
String? apkPath;
|
||
|
||
if (errorMessage.contains('APK 文件位于:')) {
|
||
final parts = errorMessage.split('APK 文件位于:');
|
||
if (parts.length > 1) {
|
||
apkPath = parts[1].trim();
|
||
showApkPath = true;
|
||
errorMessage = '您取消了安装';
|
||
}
|
||
}
|
||
|
||
_showErrorDialog(context, errorMessage, showApkPath: showApkPath, apkPath: apkPath);
|
||
}
|
||
},
|
||
child: AlertDialog(
|
||
title: const Text('发现新版本'),
|
||
content: SingleChildScrollView(
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text('版本号: ${versionInfo.version}'),
|
||
const SizedBox(height: 8),
|
||
Text('更新类型: ${versionInfo.updateType == "patch" ? "差量更新" : "整包更新"}'),
|
||
const SizedBox(height: 8),
|
||
if (versionInfo.forceUpdate)
|
||
Container(
|
||
padding: const EdgeInsets.all(8),
|
||
decoration: BoxDecoration(
|
||
color: Colors.red.shade50,
|
||
borderRadius: BorderRadius.circular(4),
|
||
),
|
||
child: const Text(
|
||
'⚠️ 强制更新',
|
||
style: TextStyle(color: Colors.red),
|
||
),
|
||
),
|
||
const SizedBox(height: 12),
|
||
const Text('更新内容:', style: TextStyle(fontWeight: FontWeight.bold)),
|
||
const SizedBox(height: 4),
|
||
Text(versionInfo.updateDesc.isEmpty ? '优化用户体验,修复已知问题' : versionInfo.updateDesc),
|
||
],
|
||
),
|
||
),
|
||
actions: [
|
||
if (!versionInfo.forceUpdate)
|
||
TextButton(
|
||
onPressed: () {
|
||
context.read<UpdateCubit>().cancelUpdate();
|
||
Navigator.of(context).pop();
|
||
},
|
||
child: const Text('稍后'),
|
||
),
|
||
BlocBuilder<UpdateCubit, UpdateState>(
|
||
builder: (context, state) {
|
||
if (state is UpdateDownloading) {
|
||
return Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Text(
|
||
state.isPatch ? '⬇️ 正在下载补丁...' : '⬇️ 正在下载 APK...',
|
||
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold),
|
||
),
|
||
const SizedBox(height: 8),
|
||
SizedBox(
|
||
width: 60,
|
||
height: 60,
|
||
child: Stack(
|
||
alignment: Alignment.center,
|
||
children: [
|
||
CircularProgressIndicator(
|
||
value: state.progress,
|
||
strokeWidth: 3,
|
||
),
|
||
Text('${(state.progress * 100).toInt()}%'),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
);
|
||
} else if (state is UpdateClearingVersion) {
|
||
return const Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Text('✅ 版本号清理完成', style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
|
||
SizedBox(height: 4),
|
||
Text('正在调起安装页面...', style: TextStyle(fontSize: 12)),
|
||
SizedBox(height: 8),
|
||
CircularProgressIndicator(),
|
||
],
|
||
);
|
||
} else if (state is UpdateReadyToInstall) {
|
||
return ElevatedButton.icon(
|
||
onPressed: () {
|
||
context.read<UpdateCubit>().triggerInstall(state.apkPath);
|
||
},
|
||
icon: const Icon(Icons.install_mobile),
|
||
label: const Text('去安装'),
|
||
);
|
||
} else if (state is UpdateInstalling) {
|
||
return Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
const Text('📦 正在打开安装页面...', style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
|
||
const SizedBox(height: 4),
|
||
const Text('请在系统安装界面确认安装', style: TextStyle(fontSize: 12)),
|
||
const SizedBox(height: 8),
|
||
const CircularProgressIndicator(),
|
||
],
|
||
);
|
||
} else {
|
||
return ElevatedButton(
|
||
onPressed: () {
|
||
if (versionInfo.updateType == 'patch' && versionInfo.patchUrl != null) {
|
||
context.read<UpdateCubit>().applyPatch(
|
||
versionInfo.patchUrl!,
|
||
versionInfo.version,
|
||
versionInfo.versionCode,
|
||
md5: versionInfo.patchMd5,
|
||
);
|
||
} else if (versionInfo.apkUrl != null) {
|
||
context.read<UpdateCubit>().downloadAndInstallApk(versionInfo.apkUrl!);
|
||
}
|
||
},
|
||
child: const Text('立即更新'),
|
||
);
|
||
}
|
||
},
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
void _showRestartDialog(BuildContext context) {
|
||
showDialog(
|
||
context: context,
|
||
barrierDismissible: false,
|
||
builder: (ctx) => AlertDialog(
|
||
title: const Text('更新完成'),
|
||
content: const Text('差量更新已应用,需要重启应用才能生效。\n是否立即重启?'),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () => Navigator.of(ctx).pop(),
|
||
child: const Text('稍后'),
|
||
),
|
||
ElevatedButton(
|
||
onPressed: () {
|
||
Navigator.of(ctx).pop();
|
||
SystemNavigator.pop();
|
||
},
|
||
child: const Text('立即重启'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
void _showErrorDialog(BuildContext context, String message, {bool showApkPath = false, String? apkPath}) {
|
||
showDialog(
|
||
context: context,
|
||
builder: (ctx) => AlertDialog(
|
||
title: const Text('更新提示'),
|
||
content: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(message),
|
||
if (showApkPath && apkPath != null) ...[
|
||
const SizedBox(height: 12),
|
||
Container(
|
||
padding: const EdgeInsets.all(10),
|
||
decoration: BoxDecoration(
|
||
color: Colors.blue.shade50,
|
||
borderRadius: BorderRadius.circular(6),
|
||
border: Border.all(color: Colors.blue.shade200),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Text(
|
||
'📁 APK 文件位置',
|
||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 13),
|
||
),
|
||
const SizedBox(height: 6),
|
||
Text(
|
||
apkPath,
|
||
style: const TextStyle(fontSize: 11, fontFamily: 'monospace'),
|
||
),
|
||
const SizedBox(height: 6),
|
||
const Text(
|
||
'您可以到该目录手动点击 APK 文件进行安装',
|
||
style: TextStyle(fontSize: 11, color: Colors.grey),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
],
|
||
),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () => Navigator.of(ctx).pop(),
|
||
child: const Text('确定'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 显示整包更新提示(在浏览器中下载)
|
||
class FullApkUpdateDialog extends StatelessWidget {
|
||
final AppVersionInfo versionInfo;
|
||
|
||
const FullApkUpdateDialog({super.key, required this.versionInfo});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return AlertDialog(
|
||
title: const Text('发现新版本'),
|
||
content: SingleChildScrollView(
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text('版本号: ${versionInfo.version}'),
|
||
const SizedBox(height: 8),
|
||
const Text('更新类型: 整包更新'),
|
||
const SizedBox(height: 12),
|
||
Container(
|
||
padding: const EdgeInsets.all(12),
|
||
decoration: BoxDecoration(
|
||
color: Colors.orange.shade50,
|
||
borderRadius: BorderRadius.circular(8),
|
||
border: Border.all(color: Colors.orange.shade200),
|
||
),
|
||
child: const Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
'📱 整包更新步骤',
|
||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
|
||
),
|
||
SizedBox(height: 8),
|
||
Text(
|
||
'1️⃣ 点击"立即下载"打开浏览器\n'
|
||
'2️⃣ 等待 APK 下载完成\n'
|
||
'3️⃣ 手动安装新版本的 APK\n'
|
||
'4️⃣ 安装完成后,卸载旧版本 APP\n'
|
||
'5️⃣ 重新打开新版本 APP',
|
||
style: TextStyle(fontSize: 13, height: 1.6),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
const SizedBox(height: 12),
|
||
if (versionInfo.updateDesc.isNotEmpty) ...[
|
||
const Text('更新内容:', style: TextStyle(fontWeight: FontWeight.bold)),
|
||
const SizedBox(height: 4),
|
||
Text(versionInfo.updateDesc),
|
||
],
|
||
],
|
||
),
|
||
),
|
||
actions: [
|
||
if (!versionInfo.forceUpdate)
|
||
TextButton(
|
||
onPressed: () {
|
||
context.read<UpdateCubit>().cancelUpdate();
|
||
Navigator.of(context).pop();
|
||
},
|
||
child: const Text('稍后'),
|
||
),
|
||
ElevatedButton(
|
||
onPressed: () {
|
||
if (versionInfo.apkUrl != null) {
|
||
// 🔥 调用新方法:浏览器下载 + 清除补丁记录
|
||
context.read<UpdateCubit>().fullApkUpdateWithBrowser(versionInfo.apkUrl!);
|
||
Navigator.of(context).pop();
|
||
|
||
// 显示后续引导弹窗
|
||
_showManualInstallGuide(context);
|
||
}
|
||
},
|
||
child: const Text('立即下载'),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
/// 显示手动安装引导弹窗
|
||
void _showManualInstallGuide(BuildContext context) {
|
||
Future.delayed(const Duration(milliseconds: 500), () {
|
||
showDialog(
|
||
context: context,
|
||
barrierDismissible: false,
|
||
builder: (ctx) => AlertDialog(
|
||
title: const Text('🔔 重要提示'),
|
||
content: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Text(
|
||
'请在浏览器中完成以下操作:',
|
||
style: TextStyle(fontWeight: FontWeight.bold),
|
||
),
|
||
const SizedBox(height: 12),
|
||
const Text('✅ 等待 APK 下载完成'),
|
||
const SizedBox(height: 8),
|
||
const Text('✅ 点击 APK 文件进行安装'),
|
||
const SizedBox(height: 8),
|
||
const Text('✅ 安装完成后,卸载当前旧版本'),
|
||
const SizedBox(height: 8),
|
||
const Text('✅ 重新打开新版本 APP'),
|
||
const SizedBox(height: 12),
|
||
Container(
|
||
padding: const EdgeInsets.all(10),
|
||
decoration: BoxDecoration(
|
||
color: Colors.red.shade50,
|
||
borderRadius: BorderRadius.circular(6),
|
||
border: Border.all(color: Colors.red.shade200),
|
||
),
|
||
child: const Text(
|
||
'⚠️ 注意:必须先卸载旧版本,否则无法正常使用新功能!',
|
||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
actions: [
|
||
ElevatedButton(
|
||
onPressed: () {
|
||
Navigator.of(ctx).pop();
|
||
},
|
||
child: const Text('我知道了'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
});
|
||
}
|
||
}
|
||
|