yx_app_upgrade_flutter/README.md

355 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# YX App Upgrade Flutter
一款轻量、现代且易用的 Flutter 应用内更新插件。支持 Android 的"下载-安装"全流程iOS 自动跳转 App Store。提供「一键检查更新」与「静默检查 + 用户决定」两种常见用法,并内置完善的权限处理与安装策略。
## ✨ 特性
- **🎯 智能平台适配**Android 直下直装iOS 跳转 App Store
- **🧠 智能辅助工具**:内置 `UpgradeAuxiliaryUtils`,支持**设备类型自动识别**与**"稍后更新"状态记忆**(同一版本被跳过不再打扰)。
- **🔄 灵活的更新策略**
- **一键检查**:自动拉取、对比版本、弹窗提示
- **静默检查**:后台获取更新信息,适合冷启动或用户主动点击前的预检查
- **🎨 现代化 UI**
- Material 风格对话框
- **📝 富文本支持**:更新日志支持粗体、斜体、代码块、高亮等格式
- 进度与状态可视化
- **🛡️ 权限适配完善**:针对不同 Android 版本的存储、安装、通知权限自动处理
- **🌐 网络可配置**证书校验、超时、默认方法、Headers 等
- **🔧 安装策略灵活**:系统流程/预检查权限/智能策略可选
- **🏪 应用市场支持**:支持多应用市场白名单,智能检测设备已安装的市场
- **📦 多种更新方式**:应用市场、浏览器下载、应用内下载
## 📦 安装
`pubspec.yaml` 中添加依赖:
```yaml
dependencies:
yx_app_upgrade_flutter: ^1.0.0
```
## 🚀 快速开始
### 方式一:使用辅助工具检查(推荐)
使用 `UpgradeAuxiliaryUtils` 可以自动处理设备类型判断,并记住用户的"稍后更新"选择(若用户对当前版本点击了稍后更新,下次检查将自动跳过,避免重复打扰)。
```dart
import 'package:yx_app_upgrade_flutter/yx_app_upgrade_flutter.dart';
void checkUpdate(BuildContext context) {
UpgradeAuxiliaryUtils.instance.initiateVersionCheck(
context,
// upType: 1 为 Android, 2 为 iOS
future: (int upType) async {
// 模拟请求 API
// final response = await api.checkVersion(type: upType);
return AppUpgradeVersion(
versionName: "1.2.0",
versionBuildNumber: 102,
downloadUrl: "https://example.com/app.apk",
updateContent: "1. 新增功能\n2. 修复Bug",
// ... 其他配置
);
},
// 可选配置
showNoUpdateToast: true, // 无更新时是否提示
config: UpgradeConfig.production, // 指定环境配置
);
}
```
### 方式二:基础 API (AppUpgradeSimple)
如果您需要更底层的控制,或者不需要"稍后更新"的记忆功能,可以直接使用核心类 `AppUpgradeSimple`
#### 1. 一键检查更新
```dart
import 'package:yx_app_upgrade_flutter/app_upgrade_simple.dart';
// ...
AppUpgradeSimple.instance.checkUpdate(
context: context,
future: () async {
// 调用 API 获取版本信息
return AppUpgradeVersion(
// ...
);
},
);
```
#### 2. 静默检查 + 由用户决定
此方式适用于 App 启动时在后台静默检查,仅在有更新时(如显示红点)提示用户。
```dart
// 1. 静默检查更新(不显示任何 UI
final upgradeInfo = await AppUpgradeSimple.instance.silentCheckUpdate(
future: () async {
return AppUpgradeVersion(...);
},
);
// 2. 根据结果处理
if (upgradeInfo != null && upgradeInfo.hasUpdate) {
print('发现新版本: ${upgradeInfo.versionName}');
// 显示红点,或者在用户点击"检查更新"按钮时调用:
AppUpgradeSimple.instance.showPreparedUpgrade(
context: context,
info: upgradeInfo,
);
}
```
### 常用配置
```dart
// 预设配置:
AppUpgradeSimple.instance.configure(UpgradeConfig.development); // 开发模式(详细日志+提示)
AppUpgradeSimple.instance.configure(UpgradeConfig.production); // 生产模式(静默+性能优化)
// 自定义配置:
AppUpgradeSimple.instance.configure(const UpgradeConfig(
showNoUpdateToast: true,
autoInstall: false,
installTimeout: 60,
enableDebugLog: true,
requireInstallPermission: false,
));
```
## 🎨 UI 能力与富文本
### 富文本更新日志
更新内容 (`updateContent`) 支持简单的 Markdown 风格富文本:
- **粗体**`**重要内容**`
- **斜体**`__斜体内容__`
- **代码块**`` `version 2.0` ``
- **高亮**`[特别注意]`
支持嵌套组合使用,例如:
- `**[特别注意]**`
- `__**重要说明**__`
- `请使用 \`version 2.0\` 进行灰度验证`
```text
1. 新增 **深色模式** 支持
2. 修复 `Login` 页面崩溃问题
3. [推荐] 性能大幅优化
```
### 头部说明块 `/{ ... /}`
从当前版本开始,`updateContent` 支持一个可选的头部说明区域,用于展示“本次版本概述 / 重点提示 / 总结说明”等内容。
头部块语法:
- 起始标记:`/{`
- 结束标记:`/}`
- 头部块是可选的
- 头部块可以为空
- 头部块内继续支持现有富文本标记与嵌套组合
#### 多行写法
```text
/{
本次 **V2.2.0** 版本围绕[使用体验]与[学习管理]进行了多项优化。
__请优先关注__ 工作录入与成长中心改动。
/}
1. 新增草稿箱功能
2. 优化多账号登录机制
```
#### 单行写法
```text
/{ 本次版本重点优化登录体验与录入效率 /}
1. 新增草稿箱功能
2. 修复已知问题
```
#### 空头部块写法
```text
/{ /}
1. 第一条更新说明
2. 第二条更新说明
```
#### 头部块中的嵌套示例
```text
/{
**重要内容**
__[特别注意]__
`version 2.0`
普通说明与 **[高亮重点]** 混合展示
/}
```
### 推荐的 `updateContent` 写法
建议服务端直接返回结构清晰的字符串,头部概述和正文分开:
```text
/{
本次 **V2.2.0** 版本围绕[使用体验]、[账号安全]、[家校联动]、[学习管理]等方面进行多项优化与功能新增,具体更新内容如下:
/}
1. 工作录入新增**草稿箱功能**,录入内容自动暂存;
2. 优化**多账号登录机制**,同一手机号绑定多个账号时可手动选择;
3. 全新上线**家校互动板块**
4. **成长中心**全面调整优化。
```
### 解析规则说明
- 只有当 `updateContent` 的第一段内容以 `/{` 开始时,才会被识别为头部说明块
- 头部说明块中的内容会单独显示在正文上方
- 头部中的 `** / __ / \` / []` 标记会继续走现有富文本渲染逻辑
- 如果正文行本身已经包含 `1.`、`一、`、`-`、`•` 等前缀,插件不会再额外补一个圆点,避免双重列表样式
- 如果缺少闭合标记 `/}`,插件会回退为普通正文处理,不会抛出异常
### 调试与预解析
如果你希望在展示前先检查解析结果,可以使用:
```dart
final parsed = AppUpgradeSimple.parseUpdateContent(updateContent);
debugPrint('hasExplicitHeaderBlock: ${parsed.hasExplicitHeaderBlock}');
debugPrint('header: ${parsed.header}');
debugPrint('bodyItems: ${parsed.bodyItems.map((e) => e.text).toList()}');
```
返回结果说明:
- `parsed.header`:头部说明内容
- `parsed.hasExplicitHeaderBlock`:是否显式使用了 `/{ ... /}` 头部块
- `parsed.bodyItems`:正文条目列表
- `parsed.bodyItems[i].hasLeadingMarker`:该条正文是否已经自带列表前缀
### 对话框特性
- **发现新版本**(强制/非强制)
- **版本信息卡片**
- **下载进度展示**
- **安装状态检测**
## ⚙️ Android 配置
### 1) 权限
`android/app/src/main/AndroidManifest.xml` 中添加:
```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- Android 13+ 通知权限(可选) -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<!-- Android 9 写储存权限SDK 28 及以下需要) -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application>
<!-- FileProviderAndroid 7.0+ APK 安装必需) -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
<!-- Android 11+ 查询声明:允许打开浏览器处理 HTTP/HTTPS URL -->
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
</intent>
</queries>
</manifest>
```
### 2) FileProvider 路径
创建 `android/app/src/main/res/xml/file_paths.xml`
```xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="." />
<files-path name="files" path="." />
<cache-path name="cache" path="." />
<external-cache-path name="external_cache" path="." />
<external-path name="downloads" path="Download/" />
<external-files-path name="external_app_files" path="." />
</paths>
```
## 📡 服务端返回协议
服务端需要返回包含以下字段的 JSON并在前端转换为 `AppUpgradeVersion` 对象:
```json
{
"isForceUpdate": false,
"versionBuildNumber": 101,
"versionName": "1.0.1",
"updateContent": "1. 新增 **深色模式**\n2. 修复 `Bug`",
"downloadUrl": "https://your-cdn.com/app-release.apk",
"appStoreUrl": "https://apps.apple.com/app/id123456789",
"apkSize": 25165824,
"apkMd5": "d41d8cd98f00b204e9800998ecf8427e",
"appMarkets": ["huawei", "xiaomi", "oppo"],
"supportedMethods": ["market", "browser", "inApp"]
}
```
## 🔧 进阶能力
### 1) 网络配置
```dart
// 自动选择
AppUpgradePlugin().configureHttp(HttpConfig.auto);
// 手动配置
AppUpgradePlugin().configureHttp(const HttpConfig(
ignoreCertificate: false,
enableLog: true,
connectTimeout: 30,
headers: {'Authorization': 'Bearer xxx'},
));
```
### 2) 预下载 APK
```dart
final apkPath = await AppUpgradeSimple.instance.preDownloadApk(
url: 'https://...',
onProgress: (progress) => print('${progress.percentage}%'),
);
```
## 🤝 贡献
欢迎提交 Issue 与 Pull Request
## 📄 许可证
MIT License