补充提交 编写readme文档
This commit is contained in:
parent
ba4bfe02cc
commit
7eff3553fb
297
README.md
297
README.md
|
|
@ -5,13 +5,18 @@
|
||||||
## ✨ 特性
|
## ✨ 特性
|
||||||
|
|
||||||
- **🎯 智能平台适配**:Android 直下直装,iOS 跳转 App Store
|
- **🎯 智能平台适配**:Android 直下直装,iOS 跳转 App Store
|
||||||
- **🔄 两种更新体验**:非强制(可后台下载)与强制更新(阻塞式对话框)
|
- **🔄 灵活的更新策略**:
|
||||||
|
- **一键检查**:自动拉取、对比版本、弹窗提示
|
||||||
|
- **静默检查**:后台获取更新信息,适合冷启动或用户主动点击前的预检查
|
||||||
|
- **🎨 现代化 UI**:
|
||||||
|
- Material 风格对话框
|
||||||
|
- **📝 富文本支持**:更新日志支持粗体、斜体、代码块、高亮等格式
|
||||||
|
- 进度与状态可视化
|
||||||
- **🛡️ 权限适配完善**:针对不同 Android 版本的存储、安装、通知权限自动处理
|
- **🛡️ 权限适配完善**:针对不同 Android 版本的存储、安装、通知权限自动处理
|
||||||
- **📱 现代化 UI**:Material 风格对话框,进度与状态可视化
|
|
||||||
- **🌐 网络可配置**:证书校验、超时、默认方法、Headers 等
|
- **🌐 网络可配置**:证书校验、超时、默认方法、Headers 等
|
||||||
- **🔧 安装策略灵活**:系统流程/预检查权限/智能策略可选
|
- **🔧 安装策略灵活**:系统流程/预检查权限/智能策略可选
|
||||||
- **🏪 应用市场支持**:支持多应用市场白名单,智能检测设备已安装的市场
|
- **🏪 应用市场支持**:支持多应用市场白名单,智能检测设备已安装的市场
|
||||||
- **📦 三种更新方式**:应用市场、浏览器下载、应用内下载
|
- **📦 多种更新方式**:应用市场、浏览器下载、应用内下载
|
||||||
|
|
||||||
## 📦 安装
|
## 📦 安装
|
||||||
|
|
||||||
|
|
@ -58,9 +63,11 @@ void checkUpdate(BuildContext context) {
|
||||||
appStoreUrl: data['appStoreUrl'],
|
appStoreUrl: data['appStoreUrl'],
|
||||||
apkSize: data['apkSize'],
|
apkSize: data['apkSize'],
|
||||||
apkMd5: data['apkMd5'],
|
apkMd5: data['apkMd5'],
|
||||||
|
// 应用市场配置
|
||||||
appMarkets: (data['appMarkets'] as List?)
|
appMarkets: (data['appMarkets'] as List?)
|
||||||
?.map((e) => AppMarket.fromString(e))
|
?.map((e) => AppMarket.fromString(e))
|
||||||
.toList(),
|
.toList(),
|
||||||
|
// 支持的更新方式
|
||||||
supportedMethods: [
|
supportedMethods: [
|
||||||
AppUpgradeMethod.market,
|
AppUpgradeMethod.market,
|
||||||
AppUpgradeMethod.browser,
|
AppUpgradeMethod.browser,
|
||||||
|
|
@ -74,27 +81,28 @@ void checkUpdate(BuildContext context) {
|
||||||
|
|
||||||
### 方式二:静默检查 + 由用户决定
|
### 方式二:静默检查 + 由用户决定
|
||||||
|
|
||||||
先静默检查是否有更新,然后由用户决定是否显示升级对话框:
|
此方式适用于:
|
||||||
|
1. **App 启动时**:在后台静默检查是否有更新,如果有则在合适的时机(如用户点击"版本更新"按钮时)展示,避免打断用户操作。
|
||||||
|
2. **红点提示**:检查到有更新时仅显示红点,用户点击后才弹出对话框。
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
// 注意:当前版本不提供独立的静默检查方法
|
// 1. 静默检查更新(不显示任何 UI)
|
||||||
// 如果需要静默检查,请直接调用您的 API,然后手动判断是否需要显示对话框
|
final upgradeInfo = await AppUpgradeSimple.instance.silentCheckUpdate(
|
||||||
|
future: () async {
|
||||||
|
// 调用您的 API
|
||||||
|
return AppUpgradeVersion(...);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
final response = await http.get('https://your-api.com/check-update');
|
// 2. 根据结果处理
|
||||||
final data = json.decode(response.body);
|
if (upgradeInfo != null && upgradeInfo.hasUpdate) {
|
||||||
|
// 有新版本,可以显示红点或在用户点击时调用弹窗
|
||||||
// 手动比较版本
|
print('发现新版本: ${upgradeInfo.versionName}');
|
||||||
final serverVersion = data['versionBuildNumber'];
|
|
||||||
final currentVersion = await AppUpgradeSimple.instance.getAppInfo();
|
// 在需要展示弹窗的时机(如用户点击按钮):
|
||||||
final currentBuildNumber = int.parse(currentVersion['buildNumber'] ?? '0');
|
AppUpgradeSimple.instance.showPreparedUpgrade(
|
||||||
|
|
||||||
if (serverVersion > currentBuildNumber) {
|
|
||||||
// 有新版本,显示升级对话框
|
|
||||||
await AppUpgradeSimple.instance.checkUpdate(
|
|
||||||
context: context,
|
context: context,
|
||||||
future: () async {
|
info: upgradeInfo, // 传入刚才获取的 info
|
||||||
return AppUpgradeVersion.fromJson(data);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -116,16 +124,32 @@ AppUpgradeSimple.instance.configure(const UpgradeConfig(
|
||||||
));
|
));
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🎨 UI 能力
|
## 🎨 UI 能力与富文本
|
||||||
|
|
||||||
- **发现新版本对话框**(强制/非强制)
|
### 富文本更新日志
|
||||||
|
|
||||||
|
更新内容 (`updateContent`) 支持简单的 Markdown 风格富文本,让更新日志更清晰:
|
||||||
|
|
||||||
|
- **粗体**:`**重要内容**`
|
||||||
|
- **斜体**:`__斜体内容__`
|
||||||
|
- **代码块**:`` `version 2.0` ``
|
||||||
|
- **高亮**:`[特别注意]`
|
||||||
|
|
||||||
|
示例:
|
||||||
|
```text
|
||||||
|
1. 新增 **深色模式** 支持
|
||||||
|
2. 修复 `Login` 页面崩溃问题
|
||||||
|
3. [推荐] 性能大幅优化
|
||||||
|
```
|
||||||
|
|
||||||
|
### 对话框特性
|
||||||
|
|
||||||
|
- **发现新版本**(强制/非强制)
|
||||||
- 强制更新:不可关闭,必须更新
|
- 强制更新:不可关闭,必须更新
|
||||||
- 非强制更新:可稍后更新,支持后台下载
|
- 非强制更新:可稍后更新,支持后台下载
|
||||||
- **版本信息卡片**:显示当前版本、新版本、APK 大小
|
- **版本信息卡片**:显示当前版本、新版本、APK 大小
|
||||||
- **下载进度展示**:实时显示下载进度百分比和状态
|
- **下载进度展示**:实时显示下载进度百分比和状态
|
||||||
- **安装状态检测**:自动检测安装结果,支持重试
|
- **安装状态检测**:自动检测安装结果,支持重试
|
||||||
- **更新内容展示**:支持 Markdown 格式(粗体、斜体、代码块等)
|
|
||||||
- **Android 更新方式选择**:应用市场、浏览器下载、应用内下载
|
|
||||||
|
|
||||||
## ⚙️ Android 配置
|
## ⚙️ Android 配置
|
||||||
|
|
||||||
|
|
@ -192,34 +216,16 @@ AppUpgradeSimple.instance.configure(const UpgradeConfig(
|
||||||
</paths>
|
</paths>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3) Gradle(可选)
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
android {
|
|
||||||
compileSdk 34
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
|
||||||
isCoreLibraryDesugaringEnabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📡 服务端返回协议
|
## 📡 服务端返回协议
|
||||||
|
|
||||||
服务端需要返回包含以下字段的 JSON(关键字段必须):
|
服务端需要返回包含以下字段的 JSON:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"isForceUpdate": false,
|
"isForceUpdate": false,
|
||||||
"versionBuildNumber": 101,
|
"versionBuildNumber": 101,
|
||||||
"versionName": "1.0.1",
|
"versionName": "1.0.1",
|
||||||
"updateContent": "1. 修复登录\n2. 优化UI\n3. 提升性能",
|
"updateContent": "1. 新增 **深色模式**\n2. 修复 `Bug`",
|
||||||
"downloadUrl": "https://your-cdn.com/app-release.apk",
|
"downloadUrl": "https://your-cdn.com/app-release.apk",
|
||||||
"appStoreUrl": "https://apps.apple.com/app/id123456789",
|
"appStoreUrl": "https://apps.apple.com/app/id123456789",
|
||||||
"apkSize": 25165824,
|
"apkSize": 25165824,
|
||||||
|
|
@ -229,91 +235,29 @@ dependencies {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**字段说明:**
|
### 关键字段说明
|
||||||
|
|
||||||
- `versionBuildNumber`(必填):版本号(整数),用于版本比较
|
- **versionBuildNumber**(必填):版本号(整数),用于比较。如果大于当前版本号,则提示更新。
|
||||||
- `versionName`(必填):版本名称(字符串),如 "1.0.1"
|
- **versionName**(必填):版本名称,如 "1.0.1"。如果 BuildNumber 相同但 VersionName 更大(字典序),也会提示更新。
|
||||||
- `isForceUpdate`(可选):是否强制更新,默认 `false`
|
- **appMarkets**(可选):Android 应用市场白名单。配置后,优先尝试跳转已安装且在白名单中的市场。
|
||||||
- `updateContent`(可选):更新内容说明,支持换行和 Markdown 格式
|
- **supportedMethods**(可选):指定支持的更新方式(market/browser/inApp)。
|
||||||
- `downloadUrl`(Android 必填):APK 下载地址
|
|
||||||
- `appStoreUrl`(iOS 必填):App Store 链接
|
|
||||||
- `apkSize`(可选):APK 文件大小(字节)
|
|
||||||
- `apkMd5`(可选):APK 文件的 MD5 值,用于校验
|
|
||||||
- `appMarkets`(可选):应用市场白名单,见下方说明
|
|
||||||
- `supportedMethods`(可选):支持的更新方式,默认全部支持
|
|
||||||
|
|
||||||
**版本比较逻辑:**
|
|
||||||
|
|
||||||
插件会优先比较 `versionBuildNumber`,如果相同则比较 `versionName`。如果服务端版本大于当前版本,则判定为有新版本。
|
|
||||||
|
|
||||||
### 应用市场白名单 (appMarkets)
|
|
||||||
|
|
||||||
`appMarkets` 用于限制支持的应用市场(Android),作为白名单使用。配置后,插件会:
|
|
||||||
|
|
||||||
1. **检测设备已安装的应用市场**
|
|
||||||
2. **匹配白名单**:只允许跳转到白名单中且设备已安装的应用市场
|
|
||||||
3. **智能处理**:
|
|
||||||
- 如果设备上没有白名单中的任何应用市场,提示用户"不支持当前设备的应用市场",引导用户选择其他更新方式(如浏览器下载、应用内下载)
|
|
||||||
- 如果设备有白名单中的应用市场,直接跳转到设备默认应用市场
|
|
||||||
|
|
||||||
**配置示例:**
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// Dart 代码
|
|
||||||
appMarkets: [AppMarket.huawei, AppMarket.xiaomi, AppMarket.oppo]
|
|
||||||
|
|
||||||
// 服务端返回
|
|
||||||
"appMarkets": ["huawei", "xiaomi", "oppo"]
|
|
||||||
```
|
|
||||||
|
|
||||||
**支持的应用市场类型:**
|
|
||||||
|
|
||||||
| 值 | 显示名称 | 对应包名 |
|
|
||||||
|---|---------|---------|
|
|
||||||
| `googlePlay` | Google Play | `com.android.vending` |
|
|
||||||
| `huawei` | 华为应用市场 | `com.huawei.appmarket` |
|
|
||||||
| `xiaomi` | 小米应用商店 | `com.xiaomi.market` |
|
|
||||||
| `oppo` | OPPO软件商店 | `com.oppo.market` |
|
|
||||||
| `vivo` | vivo应用商店 | `com.bbk.appstore` |
|
|
||||||
| `tencent` | 腾讯应用宝 | `com.tencent.android.qqdownloader` |
|
|
||||||
| `coolapk` | 酷安 | `com.coolapk.market` |
|
|
||||||
|
|
||||||
**不配置 appMarkets 的行为:**
|
|
||||||
|
|
||||||
如果不配置 `appMarkets`,插件会使用默认行为:跳转到设备的默认应用市场(使用 `market://` 协议)。
|
|
||||||
|
|
||||||
### 支持的更新方式 (supportedMethods)
|
|
||||||
|
|
||||||
`supportedMethods` 用于指定支持的更新方式,可选值:
|
|
||||||
|
|
||||||
- `market`:应用市场更新
|
|
||||||
- `browser`:浏览器下载
|
|
||||||
- `inApp`:应用内下载
|
|
||||||
|
|
||||||
如果不配置,默认支持所有方式。如果只配置一种方式,将直接使用该方式,不会显示选择对话框。
|
|
||||||
|
|
||||||
## 🔧 进阶能力
|
## 🔧 进阶能力
|
||||||
|
|
||||||
### 1) 网络配置
|
### 1) 网络配置
|
||||||
|
|
||||||
|
支持开发/生产环境切换,以及自定义 HTTP 配置(如 Headers、超时):
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
// 自动选择(Debug 绕过证书、Release 严格校验)
|
// 自动选择(Debug 绕过证书、Release 严格校验)
|
||||||
AppUpgradePlugin().configureHttp(HttpConfig.auto);
|
AppUpgradePlugin().configureHttp(HttpConfig.auto);
|
||||||
|
|
||||||
// 开发环境配置(绕过证书验证)
|
// 手动配置:
|
||||||
AppUpgradePlugin().configureHttp(HttpConfig.development);
|
|
||||||
|
|
||||||
// 生产环境配置(严格证书验证)
|
|
||||||
AppUpgradePlugin().configureHttp(HttpConfig.production);
|
|
||||||
|
|
||||||
// 或手动配置:
|
|
||||||
AppUpgradePlugin().configureHttp(const HttpConfig(
|
AppUpgradePlugin().configureHttp(const HttpConfig(
|
||||||
ignoreCertificate: false,
|
ignoreCertificate: false,
|
||||||
enableLog: true,
|
enableLog: true,
|
||||||
connectTimeout: 30,
|
connectTimeout: 30,
|
||||||
receiveTimeout: 60,
|
headers: {'Authorization': 'Bearer xxx'},
|
||||||
defaultMethod: 'GET',
|
|
||||||
headers: {'User-Agent': 'MyApp/1.0'},
|
|
||||||
));
|
));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -327,43 +271,9 @@ final hasStorage = await PermissionHelper.checkAndRequestStoragePermission(conte
|
||||||
|
|
||||||
// 检查并请求安装权限
|
// 检查并请求安装权限
|
||||||
final hasInstall = await PermissionHelper.checkAndRequestInstallPermission(context: context);
|
final hasInstall = await PermissionHelper.checkAndRequestInstallPermission(context: context);
|
||||||
|
|
||||||
// 检查并请求通知权限
|
|
||||||
final hasNotification = await PermissionHelper.checkAndRequestNotificationPermission(context: context);
|
|
||||||
|
|
||||||
// 精确跳转安装权限设置
|
|
||||||
await AppUpgradePlugin().openInstallPermissionSettings();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**权限处理说明:**
|
### 3) 预下载 APK
|
||||||
|
|
||||||
- **存储权限**:Android 13+ 无需权限(使用应用私有目录),Android 10-12 会尝试请求但失败时使用私有目录,Android 9 及以下需要权限
|
|
||||||
- **安装权限**:默认情况下(`requireInstallPermission: false`),插件会直接调用系统安装流程,由系统处理权限检查。如果设置为 `true`,会在安装前检查权限
|
|
||||||
- **通知权限**:Android 13+ 需要,用于显示下载进度通知
|
|
||||||
|
|
||||||
### 3) 安装策略(Android)
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// 系统流程(推荐):系统处理权限并弹出安装确认
|
|
||||||
await AppUpgradePlugin().installApkWithSystemFlow(apkPath);
|
|
||||||
|
|
||||||
// 传统安装:需要事先自行处理安装权限
|
|
||||||
await AppUpgradePlugin().installApk(apkPath);
|
|
||||||
|
|
||||||
// 按配置策略安装
|
|
||||||
await AppUpgradePlugin().installApkWithConfig(
|
|
||||||
apkPath,
|
|
||||||
config: InstallConfig.smart,
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
**策略对比:**
|
|
||||||
|
|
||||||
- `systemFlow`:系统处理权限检查与确认,体验最佳(推荐)
|
|
||||||
- `preCheckPermission`:先检查权限,无权时返回错误,便于精细控制
|
|
||||||
- `smart`:有权用预检查,无权走系统流程
|
|
||||||
|
|
||||||
### 4) 预下载 APK
|
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
// 后台预下载 APK(不显示 UI)
|
// 后台预下载 APK(不显示 UI)
|
||||||
|
|
@ -373,89 +283,8 @@ final apkPath = await AppUpgradeSimple.instance.preDownloadApk(
|
||||||
print('下载进度: ${progress.percentage}%');
|
print('下载进度: ${progress.percentage}%');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (apkPath != null) {
|
|
||||||
print('下载完成: $apkPath');
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5) 查找已下载的 APK
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// 查找指定版本的已下载 APK
|
|
||||||
final apkPath = await AppUpgradeSimple.instance.findDownloadedApk('1.0.1');
|
|
||||||
if (apkPath != null) {
|
|
||||||
// 直接安装
|
|
||||||
await AppUpgradePlugin().installApkWithSystemFlow(apkPath);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6) 清理下载缓存
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// 清理所有已下载的 APK 文件
|
|
||||||
await AppUpgradeSimple.instance.clearDownloadCache();
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🐛 常见问题
|
|
||||||
|
|
||||||
- **安装失败显示"解析包时出现问题"**:检查 APK 完整性、签名与架构匹配
|
|
||||||
- **权限申请失败**:确认 Manifest 权限、FileProvider 配置、在 MaterialApp 环境调用
|
|
||||||
- **下载失败/进度不更新**:检查网络、下载 URL 可用性、服务端是否支持断点续传
|
|
||||||
- **iOS 不跳转**:确认 `appStoreUrl` 为有效的 App Store 链接
|
|
||||||
- **"前往浏览器下载"无反应**:确认已在 AndroidManifest.xml 中添加 `<queries>` 声明(Android 11+ 必需)
|
|
||||||
- **FileProvider 配置错误**:确认 `file_paths.xml` 中已添加 `<external-files-path>` 配置,用于权限被拒绝时的备用存储路径
|
|
||||||
- **Android 9 下载权限错误**:插件会自动检测权限,无权限时使用应用私有目录,无需额外配置
|
|
||||||
- **安装检测超时**:默认超时时间为 45 秒,可通过 `UpgradeConfig.installTimeout` 调整
|
|
||||||
|
|
||||||
## 📚 主要 API 清单
|
|
||||||
|
|
||||||
### AppUpgradeSimple(推荐使用)
|
|
||||||
|
|
||||||
- `configure(UpgradeConfig)`:配置升级参数
|
|
||||||
- `checkUpdate({context, future, showNoUpdateToast, autoInstall, onComplete, config})`:检查更新并显示 UI
|
|
||||||
- `preDownloadApk({url, onProgress})`:预下载 APK(Android)
|
|
||||||
- `findDownloadedApk(version)`:查找已下载的 APK(Android)
|
|
||||||
- `getAppInfo()`:获取当前应用信息
|
|
||||||
- `clearDownloadCache()`:清理下载缓存
|
|
||||||
- `checkNetworkStatus()`:检查网络连接状态
|
|
||||||
|
|
||||||
### AppUpgradePlugin(底层能力)
|
|
||||||
|
|
||||||
- `configureHttp(HttpConfig)`:网络层配置
|
|
||||||
- `checkUpdate(url, {params})`:检查更新(返回 UpgradeInfo)
|
|
||||||
- `downloadApk(url, {onProgress, savePath})`:下载 APK(Android)
|
|
||||||
- `installApk(filePath)`:安装 APK(需要先处理权限)
|
|
||||||
- `installApkWithSystemFlow(filePath)`:使用系统流程安装(推荐)
|
|
||||||
- `installApkWithConfig(filePath, {config})`:按配置策略安装
|
|
||||||
- `openInstallPermissionSettings()`:跳转安装权限设置(Android)
|
|
||||||
- `getDeviceInfo()`、`getAndroidSdkVersion()`:获取设备信息(Android)
|
|
||||||
- `goToAppStore(url, {context})`:跳转到应用商店
|
|
||||||
- `getInstalledMarkets()`:获取设备已安装的应用市场列表(Android)
|
|
||||||
- `getDownloadPath()`:获取下载目录路径
|
|
||||||
- `checkApkExists(version, md5)`:检查指定版本的 APK 是否已下载
|
|
||||||
|
|
||||||
### PermissionHelper(Android)
|
|
||||||
|
|
||||||
- `checkAndRequestStoragePermission(context)`:检查并请求存储权限
|
|
||||||
- `checkAndRequestInstallPermission(context)`:检查并请求安装权限
|
|
||||||
- `checkAndRequestNotificationPermission(context)`:检查并请求通知权限
|
|
||||||
- `checkInstallPermission()`:检查安装权限状态
|
|
||||||
|
|
||||||
### 配置类
|
|
||||||
|
|
||||||
- `UpgradeConfig`:升级配置(超时、自动安装、日志等)
|
|
||||||
- `HttpConfig`:HTTP 配置(证书、超时、Headers 等)
|
|
||||||
- `InstallConfig`:安装策略配置
|
|
||||||
|
|
||||||
### 模型类
|
|
||||||
|
|
||||||
- `AppUpgradeVersion`:服务端返回的版本信息模型
|
|
||||||
- `UpgradeInfo`:内部使用的升级信息模型
|
|
||||||
- `AppMarket`:应用市场枚举
|
|
||||||
- `AppUpgradeMethod`:更新方式枚举
|
|
||||||
- `DownloadProgress`:下载进度信息
|
|
||||||
|
|
||||||
## 🤝 贡献
|
## 🤝 贡献
|
||||||
|
|
||||||
欢迎提交 Issue 与 Pull Request!
|
欢迎提交 Issue 与 Pull Request!
|
||||||
|
|
@ -463,9 +292,3 @@ await AppUpgradeSimple.instance.clearDownloadCache();
|
||||||
## 📄 许可证
|
## 📄 许可证
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
## 🔗 参考
|
|
||||||
|
|
||||||
- [Flutter 官网](https://flutter.dev)
|
|
||||||
- [Android 安装权限文档](https://developer.android.com/reference/android/Manifest.permission#REQUEST_INSTALL_PACKAGES)
|
|
||||||
- [FileProvider 使用指南](https://developer.android.com/reference/androidx/core/content/FileProvider)
|
|
||||||
|
|
|
||||||
|
|
@ -236,71 +236,12 @@ class AppUpgradeSimple {
|
||||||
try {
|
try {
|
||||||
assert(_canShowMaterialDialog(context), '请在 MaterialApp 环境内调用');
|
assert(_canShowMaterialDialog(context), '请在 MaterialApp 环境内调用');
|
||||||
|
|
||||||
// 1. 获取服务器版本信息
|
final info = await _prepareUpgradeInfo(future: future, config: effectiveConfig);
|
||||||
final serverInfo = await future();
|
if (info == null) {
|
||||||
if (serverInfo == null) {
|
|
||||||
// 获取失败或无数据,视作无更新(已是最新)
|
|
||||||
if (effectiveConfig.enableDebugLog) {
|
|
||||||
debugPrint('🔍 检查更新结果: 未返回版本信息');
|
|
||||||
}
|
|
||||||
onComplete?.call(true);
|
onComplete?.call(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (effectiveConfig.enableDebugLog) {
|
|
||||||
debugPrint('🔍 获取到服务器版本: $serverInfo');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 获取当前App信息
|
|
||||||
final appInfo = await _plugin.getAppInfo();
|
|
||||||
final currentVersionName = appInfo['version'] ?? '';
|
|
||||||
final currentBuildNumber = int.tryParse(appInfo['buildNumber'] ?? '0') ?? 0;
|
|
||||||
|
|
||||||
if (effectiveConfig.enableDebugLog) {
|
|
||||||
debugPrint('📱 当前版本: $currentVersionName (Build: $currentBuildNumber)');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 比较版本并构建 UpgradeInfo
|
|
||||||
bool hasUpdate = false;
|
|
||||||
if (serverInfo.versionBuildNumber > 0) {
|
|
||||||
// 优先比较 buildNumber
|
|
||||||
if (serverInfo.versionBuildNumber > currentBuildNumber) {
|
|
||||||
hasUpdate = true;
|
|
||||||
} else if (serverInfo.versionBuildNumber == currentBuildNumber) {
|
|
||||||
// buildNumber 相同,比较版本名
|
|
||||||
if (_compareVersionStrings(serverInfo.versionName, currentVersionName) > 0) {
|
|
||||||
hasUpdate = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 只比较版本名
|
|
||||||
if (_compareVersionStrings(serverInfo.versionName, currentVersionName) > 0) {
|
|
||||||
hasUpdate = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (effectiveConfig.enableDebugLog) {
|
|
||||||
debugPrint('📊 版本比较结果: ${hasUpdate ? "有新版本" : "已是最新"}');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建内部使用的 UpgradeInfo
|
|
||||||
final info = UpgradeInfo(
|
|
||||||
hasUpdate: hasUpdate,
|
|
||||||
isForceUpdate: serverInfo.isForce,
|
|
||||||
versionName: serverInfo.versionName,
|
|
||||||
versionBuildNumber: serverInfo.versionBuildNumber,
|
|
||||||
currentVersionName: currentVersionName,
|
|
||||||
currentBuildNumber: currentBuildNumber,
|
|
||||||
updateContent: serverInfo.updateContent,
|
|
||||||
downloadUrl: serverInfo.downloadUrl,
|
|
||||||
appStoreUrl: serverInfo.appStoreUrl,
|
|
||||||
apkSize: serverInfo.apkSize,
|
|
||||||
apkMd5: serverInfo.apkMd5,
|
|
||||||
appMarkets: serverInfo.appMarkets,
|
|
||||||
supportedMethods: serverInfo.supportedMethods ??
|
|
||||||
const [AppUpgradeMethod.market, AppUpgradeMethod.browser, AppUpgradeMethod.inApp],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!info.hasUpdate) {
|
if (!info.hasUpdate) {
|
||||||
if (finalShowNoUpdateToast && context.mounted) {
|
if (finalShowNoUpdateToast && context.mounted) {
|
||||||
_showToast('已是最新版本', context, effectiveConfig);
|
_showToast('已是最新版本', context, effectiveConfig);
|
||||||
|
|
@ -326,6 +267,142 @@ class AppUpgradeSimple {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 静默检查更新(不弹出任何 UI)
|
||||||
|
///
|
||||||
|
/// 返回 [UpgradeInfo],其中包含服务端版本信息以及 hasUpdate 标记。
|
||||||
|
/// 场景示例:
|
||||||
|
/// - App 冷启动时后台检查更新,但不打扰用户
|
||||||
|
/// - 进入「设置-检查更新」页面前,先决定是否展示弹窗
|
||||||
|
///
|
||||||
|
/// 搭配 [showPreparedUpgrade] 可避免重复请求服务端。
|
||||||
|
Future<UpgradeInfo?> silentCheckUpdate({
|
||||||
|
required Future<AppUpgradeVersion?> Function() future,
|
||||||
|
UpgradeConfig? config,
|
||||||
|
}) async {
|
||||||
|
final effectiveConfig = config ?? _config;
|
||||||
|
try {
|
||||||
|
final info = await _prepareUpgradeInfo(future: future, config: effectiveConfig);
|
||||||
|
if (effectiveConfig.enableDebugLog) {
|
||||||
|
if (info == null) {
|
||||||
|
debugPrint('🔕 静默检查结果: 未返回版本信息');
|
||||||
|
} else {
|
||||||
|
debugPrint('🔕 静默检查完成: hasUpdate=${info.hasUpdate}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
} catch (e) {
|
||||||
|
if (effectiveConfig.enableDebugLog) {
|
||||||
|
debugPrint('静默检查更新失败: $e');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 使用已知的 [UpgradeInfo] 展示升级弹窗
|
||||||
|
///
|
||||||
|
/// 通常与 [silentCheckUpdate] 搭配:先静默检查并缓存结果,用户点击
|
||||||
|
/// 「检查更新」按钮时再调用此方法展示 UI,无需再次访问服务端。
|
||||||
|
Future<void> showPreparedUpgrade({
|
||||||
|
required BuildContext context,
|
||||||
|
required UpgradeInfo info,
|
||||||
|
bool? autoInstall,
|
||||||
|
BoolCallback? onComplete,
|
||||||
|
UpgradeConfig? config,
|
||||||
|
}) async {
|
||||||
|
final effectiveConfig = config ?? _config;
|
||||||
|
final finalAutoInstall = autoInstall ?? effectiveConfig.autoInstall;
|
||||||
|
|
||||||
|
assert(_canShowMaterialDialog(context), '请在 MaterialApp 环境内调用');
|
||||||
|
|
||||||
|
if (!info.hasUpdate) {
|
||||||
|
if (effectiveConfig.enableDebugLog) {
|
||||||
|
debugPrint('🔔 showPreparedUpgrade: 无新版本,跳过弹窗');
|
||||||
|
}
|
||||||
|
if (effectiveConfig.showNoUpdateToast && context.mounted) {
|
||||||
|
_showToast('已是最新版本', context, effectiveConfig);
|
||||||
|
}
|
||||||
|
onComplete?.call(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _showUpgradeDialog(
|
||||||
|
context: context,
|
||||||
|
info: info,
|
||||||
|
autoInstall: finalAutoInstall,
|
||||||
|
onComplete: onComplete,
|
||||||
|
config: effectiveConfig,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 内部方法:获取版本信息并构建 UpgradeInfo
|
||||||
|
Future<UpgradeInfo?> _prepareUpgradeInfo({
|
||||||
|
required Future<AppUpgradeVersion?> Function() future,
|
||||||
|
required UpgradeConfig config,
|
||||||
|
}) async {
|
||||||
|
// 1. 获取服务器版本信息
|
||||||
|
final serverInfo = await future();
|
||||||
|
if (serverInfo == null) {
|
||||||
|
if (config.enableDebugLog) {
|
||||||
|
debugPrint('🔍 检查更新结果: 未返回版本信息');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.enableDebugLog) {
|
||||||
|
debugPrint('🔍 获取到服务器版本: $serverInfo');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取当前App信息
|
||||||
|
final appInfo = await _plugin.getAppInfo();
|
||||||
|
final currentVersionName = appInfo['version'] ?? '';
|
||||||
|
final currentBuildNumber = int.tryParse(appInfo['buildNumber'] ?? '0') ?? 0;
|
||||||
|
|
||||||
|
if (config.enableDebugLog) {
|
||||||
|
debugPrint('📱 当前版本: $currentVersionName (Build: $currentBuildNumber)');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 比较版本并构建 UpgradeInfo
|
||||||
|
bool hasUpdate = false;
|
||||||
|
if (serverInfo.versionBuildNumber > 0) {
|
||||||
|
// 优先比较 buildNumber
|
||||||
|
if (serverInfo.versionBuildNumber > currentBuildNumber) {
|
||||||
|
hasUpdate = true;
|
||||||
|
} else if (serverInfo.versionBuildNumber == currentBuildNumber) {
|
||||||
|
// buildNumber 相同,比较版本名
|
||||||
|
if (_compareVersionStrings(serverInfo.versionName, currentVersionName) > 0) {
|
||||||
|
hasUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 只比较版本名
|
||||||
|
if (_compareVersionStrings(serverInfo.versionName, currentVersionName) > 0) {
|
||||||
|
hasUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.enableDebugLog) {
|
||||||
|
debugPrint('📊 版本比较结果: ${hasUpdate ? "有新版本" : "已是最新"}');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建 UpgradeInfo
|
||||||
|
return UpgradeInfo(
|
||||||
|
hasUpdate: hasUpdate,
|
||||||
|
isForceUpdate: serverInfo.isForce,
|
||||||
|
versionName: serverInfo.versionName,
|
||||||
|
versionBuildNumber: serverInfo.versionBuildNumber,
|
||||||
|
currentVersionName: currentVersionName,
|
||||||
|
currentBuildNumber: currentBuildNumber,
|
||||||
|
updateContent: serverInfo.updateContent,
|
||||||
|
downloadUrl: serverInfo.downloadUrl,
|
||||||
|
appStoreUrl: serverInfo.appStoreUrl,
|
||||||
|
apkSize: serverInfo.apkSize,
|
||||||
|
apkMd5: serverInfo.apkMd5,
|
||||||
|
appMarkets: serverInfo.appMarkets,
|
||||||
|
supportedMethods: serverInfo.supportedMethods ??
|
||||||
|
const [AppUpgradeMethod.market, AppUpgradeMethod.browser, AppUpgradeMethod.inApp],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// 预下载APK(不显示UI,后台下载)
|
/// 预下载APK(不显示UI,后台下载)
|
||||||
Future<String?> preDownloadApk({
|
Future<String?> preDownloadApk({
|
||||||
required String url,
|
required String url,
|
||||||
|
|
@ -1072,12 +1149,7 @@ mixin _UpgradeDialogLogic<T extends StatefulWidget> on State<T> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(child: _buildRichText(line, colorScheme)),
|
||||||
child: _buildRichText(
|
|
||||||
line,
|
|
||||||
colorScheme,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue