feat(client/update): 启动自动检查 + App 内下载安装(不走浏览器)
Deploy Client / build-windows (push) Successful in 2m2s
Deploy Client / build-android (push) Successful in 2m13s
Deploy Client / build-macos (push) Successful in 3m45s
Deploy Client / build-ios (push) Successful in 3m58s
Deploy Client / release-deploy (push) Successful in 2m1s

参照 jiu 补齐,但比 jiu 多做了 Android 原生安装(jiu 的 App 内下载仅 Win/mac):
- 启动自动检查:update_provider 改 AsyncNotifier,build() 延迟 3s 首查 + 每小时
  轮询;home_shell watch 拉起、listen 弹更新框;_dismissed 防反复弹(轮询重置);
  强制更新走不可关闭弹窗。设置页「检查更新」改 forceCheck()。
- App 内下载(core/update/app_updater.dart,http 流式带进度框,不再 launchUrl):
  · Android:下 APK → open_filex 拉起系统安装器(+ REQUEST_INSTALL_PACKAGES)
  · Windows:下 exe → Process.start(detached) → exit(0) 覆盖安装
  · macOS :下 zip → open 解压 + 访达显示,提示手动拖入(带系统扩展不自动 swap)
  · iOS   :降级外链(TestFlight/App Store)
  失败降级浏览器下载。新增 6 语更新进度/失败/提示文案。
- 依赖 open_filex;flutter analyze 仅 2 个既有 withOpacity info。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
wangjia
2026-07-07 17:09:29 +08:00
parent 617b43083d
commit c723bdd53e
8 changed files with 438 additions and 30 deletions
+13 -14
View File
@@ -1,11 +1,13 @@
// update_dialog.dart — 「发现新版本」更新提示弹窗。
//
// 只做「展示 + 引导下载」:点「下载更新」用 url_launcher 打开对应平台的下载直链
// (浏览器下载),不做应用内静默安装/自动重启。force_update=true 时不可关闭
// (无「稍后」按钮、点遮罩/返回也关不掉)。
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
// 点「下载更新」走 App 内下载安装(core/update/app_updater.dart,带进度框,不再开
// 浏览器);Android 装 APK、Windows 跑安装包、macOS 解压提示拖入、iOS 降级外链。
// force_update=true 时不可关闭(无「稍后」按钮、点遮罩/返回也关不掉)。
import 'dart:async';
import 'package:flutter/material.dart';
import '../core/update/app_updater.dart';
import '../l10n/app_text.dart';
import '../pangolin_theme.dart';
import '../state/update_provider.dart';
@@ -63,15 +65,12 @@ class _UpdateDialog extends StatelessWidget {
child: Text(t.updateLater, style: PangolinText.sm.copyWith(color: c.fg2, fontWeight: FontWeight.w600)),
),
TextButton(
onPressed: () async {
final url = platformDownloadUrl(info.downloadUrls);
if (url != null && url.isNotEmpty) {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
}
if (context.mounted) Navigator.of(context).pop();
onPressed: () {
// 先关本弹窗,用 Navigator 自身 context(关后仍挂载)启动 App 内下载
// (它自带进度框)。
final nav = Navigator.of(context);
nav.pop();
unawaited(startInAppUpdate(nav.context, t, info));
},
child: Text(t.updateDownload, style: PangolinText.sm.copyWith(color: c.accent, fontWeight: FontWeight.w700)),
),