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