【重磅发布】flutter_chen_updater-版本升级更新
Flutter Chen Updater
一个功能强大的Flutter应用内更新插件,支持Android APK自动下载、安装和iOS跳转App Store。
✨ 特性
- ✅ 跨平台支持: Android APK自动更新,iOS跳转App Store
- ✅ 智能下载: 支持断点续传、文件校验、多重备用方案
- ✅ 权限管理: 自动处理Android安装权限、存储权限
- ✅ 强制更新: 支持可选更新和强制更新模式
- ✅ 进度监控: 实时下载进度回调
- ✅ 文件校验: MD5文件完整性验证
- ✅ 生命周期管理: 智能处理应用前后台切换
- ✅ 自定义UI: 支持自定义更新对话框
效果预览
📦 安装
在你的 pubspec.yaml
文件中添加依赖:
dependencies:flutter_chen_updater: ^1.0.0
然后运行:
flutter pub get
⚙️ 权限配置
Android
在 android/app/src/main/AndroidManifest.xml
中添加必要权限:
<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" /><!-- 存储权限 (Android 9及以下) -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" /><!-- 安装权限 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /><!-- 网络状态权限 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
🚀 基础用法
1. 简单更新检查
import 'package:flutter_chen_updater/flutter_chen_updater.dart';void checkForUpdate() {final updateInfo = UpdateInfo(version: '1.1.0',downloadUrl: 'https://example.com/app-v1.1.0.apk',iosUrl: 'https://apps.apple.com/app/id123456789',description: '1. 修复已知问题\n2. 优化用户体验\n3. 新增功能特性',isForceUpdate: false,);Updater.checkAndUpdate(context,updateInfo,onAlreadyLatest: () => print('已是最新版本'),onConfirm: () => print('用户确认更新'),onCancel: () => print('用户取消更新'),);
}
2. 带进度监控的更新
void checkForUpdateWithProgress() {final updateInfo = UpdateInfo(version: '1.1.0',downloadUrl: 'https://example.com/app-v1.1.0.apk',description: '更新说明',fileHash: 'abc123def456', // 可选:文件MD5校验hashAlgorithm: 'md5',fileSize: 15 * 1024 * 1024, // 15MB);Updater.checkAndUpdate(context,updateInfo,onProgress: (progress) {print('下载进度: ${(progress.progress * 100).toStringAsFixed(1)}%');print('已下载: ${progress.downloaded} / ${progress.total}');},onConfirm: () => print('开始下载更新'),);
}
3. 强制更新示例
void forceUpdate() {final updateInfo = UpdateInfo(version: '2.1.0',description: '重要安全更新,请立即升级',downloadUrl: 'https://example.com/app-v2.1.0.apk',isForceUpdate: true, // 强制更新,用户无法取消);Updater.checkAndUpdate(context, updateInfo);
}
4. 自定义对话框
4.1 使用 dialogBuilder 自定义
Future<bool> customDialog(BuildContext context, UpdateInfo updateInfo) {return showDialog<bool>(context: context,barrierDismissible: !updateInfo.isForceUpdate,builder: (context) => AlertDialog(title: Text('发现新版本 ${updateInfo.version}'),content: Column(mainAxisSize: MainAxisSize.min,crossAxisAlignment: CrossAxisAlignment.start,children: [Text(updateInfo.description),if (updateInfo.fileSize != null)Padding(padding: const EdgeInsets.only(top: 8.0),child: Text('大小: ${(updateInfo.fileSize! / 1024 / 1024).toStringAsFixed(1)}MB'),),],),actions: [if (!updateInfo.isForceUpdate)TextButton(onPressed: () => Navigator.pop(context, false),child: const Text('稍后更新'),),ElevatedButton(onPressed: () => Navigator.pop(context, true),child: const Text('立即更新'),),],),) ?? false;
}void checkWithCustomDialog() {final updateInfo = UpdateInfo(version: '1.1.0',downloadUrl: 'https://example.com/app.apk',description: '重要更新说明',);Updater.checkAndUpdate(context,updateInfo,dialogBuilder: customDialog,);
}
4.2 使用 UpdateDialog 灵活配置
void checkWithFlexibleDialog() {final updateInfo = UpdateInfo(version: '1.2.0',downloadUrl: 'https://example.com/app.apk',description: '• 修复重要安全漏洞\n• 优化启动速度\n• 新增夜间模式',fileSize: 25 * 1024 * 1024, // 25MB);showDialog<bool>(context: context,barrierDismissible: !updateInfo.isForceUpdate,builder: (context) => UpdateDialog(updateInfo: updateInfo,// 自定义标题title: Container(padding: const EdgeInsets.all(16),decoration: BoxDecoration(gradient: LinearGradient(colors: [Colors.blue, Colors.purple],),),child: Row(children: [Icon(Icons.system_update, color: Colors.white),const SizedBox(width: 8),Text('重要更新 V${updateInfo.version}',style: const TextStyle(color: Colors.white,fontWeight: FontWeight.bold,),),],),),// 自定义内容content: Column(mainAxisSize: MainAxisSize.min,crossAxisAlignment: CrossAxisAlignment.start,children: [Container(padding: const EdgeInsets.all(16),child: Column(crossAxisAlignment: CrossAxisAlignment.start,children: [Text('更新内容',style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold,),),const SizedBox(height: 8),Text(updateInfo.description),const SizedBox(height: 12),if (updateInfo.fileSize != null)Container(padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 6,),decoration: BoxDecoration(color: Colors.grey.shade100,borderRadius: BorderRadius.circular(16),),child: Text('安装包大小: ${(updateInfo.fileSize! / 1024 / 1024).toStringAsFixed(1)}MB',style: Theme.of(context).textTheme.bodySmall,),),],),),],),// 自定义底部操作栏footer: Container(padding: const EdgeInsets.all(16),child: Row(children: [if (!updateInfo.isForceUpdate) ...[Expanded(child: OutlinedButton(onPressed: () => Navigator.pop(context, false),child: const Text('稍后提醒'),),),const SizedBox(width: 12),],Expanded(flex: updateInfo.isForceUpdate ? 1 : 1,child: ElevatedButton.icon(onPressed: () => Navigator.pop(context, true),icon: const Icon(Icons.download),label: const Text('立即更新'),),),],),),),);
}
5. 纯下载功能
void downloadOnly() {final updateInfo = UpdateInfo(version: '1.1.0',downloadUrl: 'https://example.com/app.apk',description: '更新包',);Updater.download(updateInfo).listen((progress) {if (progress.isCompleted && progress.filePath != null) {print('下载完成: ${progress.filePath}');// 稍后安装Updater.installApk(progress.filePath!,onSuccess: () => print('安装成功'),onError: (error) => print('安装失败: $error'),);} else if (progress.isFailed) {print('下载失败: ${progress.error}');}});
}
6. 版本比较
void checkVersionUpdate() {final currentVersion = '1.0.0';final newVersion = '1.1.0';final needUpdate = Updater.needUpdate(currentVersion, newVersion);print('是否需要更新: $needUpdate');
}
📚 API 文档
UpdateInfo 类
更新信息配置类:
class UpdateInfo {final String version; // 新版本号 (必需)final String? downloadUrl; // Android APK下载链接final String? iosUrl; // iOS App Store链接final String description; // 更新说明 (必需)final bool isForceUpdate; // 是否强制更新final String? fileHash; // 文件哈希值 (可选)final String? hashAlgorithm; // 哈希算法 (默认: 'md5')final int? fileSize; // 文件大小 (字节)final Map<String, dynamic>? extra; // 额外数据
}
Updater 类
主要更新器类:
静态方法
checkAndUpdate()
- 检查并更新应用download()
- 纯下载方法(不自动安装)installApk()
- 安装APK文件needUpdate()
- 版本比较cancelDownload()
- 取消下载dispose()
- 清理资源
DownloadProgress 类
下载进度信息:
class DownloadProgress {final int downloaded; // 已下载字节数final int total; // 总字节数final double progress; // 进度 (0.0 - 1.0)final DownloadStatus status; // 下载状态final String? error; // 错误信息final String? filePath; // 文件路径
}
DownloadStatus 枚举
enum DownloadStatus {idle, // 空闲downloading, // 下载中paused, // 暂停completed, // 完成failed, // 失败cancelled, // 取消installing // 安装中
}
🔧 高级功能
文件校验
插件支持MD5文件完整性校验:
final updateInfo = UpdateInfo(version: '1.1.0',downloadUrl: 'https://example.com/app.apk',description: '安全更新',fileHash: 'a1b2c3d4e5f6...',hashAlgorithm: 'md5',
);
权限处理
插件自动处理以下权限:
- 存储权限 (Android 9及以下)
- 安装权限 (Android 8+)
- 网络权限
对于缺失权限,插件会:
- 自动请求权限
- 引导用户到系统设置页面
- 监听应用生命周期自动重试
生命周期管理
插件智能处理应用前后台切换:
- 用户跳转权限设置后返回应用时自动重试安装
- 后台下载任务保持活跃
- 应用退出时自动清理资源
💡 错误处理
插件提供完善的错误处理机制:
Updater.checkAndUpdate(context,updateInfo,onProgress: (progress) {if (progress.isFailed) {// 处理下载失败showDialog(context: context,builder: (_) => AlertDialog(title: const Text('下载失败'),content: Text(progress.error ?? '未知错误'),actions: [TextButton(onPressed: () => Navigator.pop(context),child: const Text('确定'),),],),);}},
);
🚦 最佳实践
- 版本检查: 建议在应用启动时检查更新
- 网络判断: 在检查更新前判断网络状态
- 用户体验: 避免在关键操作时弹出更新提示
- 资源清理: 应用退出时调用
Updater.dispose()
- 错误日志: 记录更新过程中的错误信息用于调试
❓ 常见问题
Q: Android安装失败怎么办?
A: 检查是否授予了"安装未知应用"权限,插件会自动引导用户设置。
Q: 下载速度慢怎么办?
A: 插件使用Android系统下载管理器和HTTP备用方案,确保最佳下载体验。
Q: 如何支持增量更新?
A: 当前版本不支持增量更新,建议使用文件校验确保完整性。
Q: iOS如何实现自动更新?
A: iOS由于系统限制只能跳转App Store,无法实现APK式的自动更新。
⚠️ 注意事项
-
Android权限:
- Android 6.0+ 需要运行时申请存储权限
- Android 8.0+ 需要安装未知来源应用权限
- Android 10+ 使用作用域存储,无需存储权限
-
文件安全:
- 建议使用HTTPS下载链接
- 强烈推荐设置fileHash进行文件完整性校验
- 下载的APK文件会自动进行基础验证
-
用户体验:
- 避免在用户进行重要操作时弹出更新提示
- 强制更新应谨慎使用,仅在安全更新时使用
- 提供清晰的更新说明和文件大小信息
📄 许可证
MIT License