行业知识
Flutter APP下载更新
2026-10-25 23:52:43
来源:互联网转载
由于我做的项目不是放在APP商店(公司内部用)的,一些flutter的第三方库不合适我,我需要用的是从网上下载再安装(从服务下),网上也找了花了我好几天时间。不全又乱,这我自己做一下备份
()现在只使用安卓下载,ios没有做(后期可能更新)
app更新要求
1.进入app就查看app是否要更新(更新对比自己写)
()2.下载完成可以自动弹窗安装界面
正式开始
1.使用第三方库
dependencies: # 查询应用程序包信息 package_info_plus: ^5.0.1 # 创建和管理下载任务的插件 flutter_downloader: ^1.11.6 # 安装插件,打开安装界面 install_plugin: ^2.1.0 # 权限处理程序 permission_handler: ^11.3.0 # 用于比较和递增版本号 version: ^3.0.2
2.权限
添加权限
android\app\src\main\AndroidManifest.xml
manifest需要加上xmlns:tools="http://schemas.android.com/tools",
不然可能报错
如果你是HTTP下载的
允许访问明文HTTP流量
你可能要通过这个去添加http下载权限
报错关键词: Cleartext HTTP traffic to xxx not permitted
3.初始化
import 'package:flutter_downloader/flutter_downloader.dart';import './utils/update_app.dart';// 等下要做的更新方法void main() async { // 下载器 插件在使用前必须初始化 await FlutterDownloader.initialize( debug: true, // 可选:设置为false以禁用将日志打印到控制台(默认:true) ignoreSsl: true, // 选项:设置为false以禁用HTTP链接(默认值:false) ); // 更新App await updateApp(); runApp(const MyApp());} 4.更新方法
前置工作
这使用get库的二次封装弹窗,这个弹窗自行实现,不然代码太多了。主要看注释下的代码
import 'package:get/get.dart';import 'package:flutter/material.dart';/// 更新AppupdateApp() async { // 是否有最新版本 bool isUpdate = await isNewVersions(); if (!isUpdate) { debugPrint('暂不用更新'); return; } // 等页面加载完后再执行后面的,这个是重点,你刚进App大概是没有加载完页面的 WidgetsBinding.instance.addPostFrameCallback((_) async { confirmDialog( title: '更新程序', textCancel: '稍后', textConfirm: '现在更新', isVerticalLayout: false, onCancel: () => Get.back(),// 关闭弹窗 onConfirm: () async { // 下载监听 bindBackgroundIsolate(); // 下载 await downloaderApp(); // 这是另一个方法,后面讲 // _networkInstallApk(); }, ); });}/// 检查是否有新的版本要更新isNewVersions() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); Version version = Version.parse(packageInfo.version); // 请求过来的版本号,自行请求,或者你有别的实现方法 Version request = Version.parse('0.3.0'); debugPrint('APP_版本:$version,请求_版本:$request,是否有最新的版本:${version version;} 方法1,使用flutter_downloader下载
其实我这下面与上面的代码放一起的,比较方便,当然主要还是看个人怎么做
import 'dart:isolate';import 'dart:ui';import 'package:cpm/utils/gadget.dart';import 'package:flutter/material.dart';import 'package:flutter_downloader/flutter_downloader.dart';import 'package:install_plugin/install_plugin.dart';final ReceivePort _port = ReceivePort(); // 声明接收端口// 下载文件地址,这个是install_plugin库提供的apk文件地址,可以测试使用String url = 'https://s3.cn-north-1.amazonaws.com.cn/mtab.kezaihui.com/apk/takeaway_phone_release_1.apk';String fileName = 'downloader_send_port'; //文件名final isDown = false.obs; // 下载状态dynamic taskId = 0; // 文件下载IDString savedDir = ''; // 本地文件夹路径RxString percent = '0'.obs; // 下载进度,这我是使用Get库的状态管理准备显示在页面的// 下载文档Future downloaderApp() async { debugPrint('准备下载。检查有没有存储权限'); bool isStorage = await checkPermissionStorage(); if (!isStorage) { debugPrint('没有存储权限'); return; } savedDir = await findLocalPath(); debugPrint('下载中...'); isDown.value = true; taskId = await FlutterDownloader.enqueue( url: url, // 链接文件下载 savedDir: savedDir, // 本地文件夹路径 fileName: fileName, //文件名 showNotification: true, // 在状态栏显示下载进度(适用于Android) openFileFromNotification: true, // 点击通知打开下载的文件(适用于Android) ); // 更新下载进度 await FlutterDownloader.registerCallback(Download.downloadCallback);}/// 下载监听void bindBackgroundIsolate() { final isSuccess = IsolateNameServer.registerPortWithName(_port.sendPort, fileName); if (!isSuccess) { _unbindBackgroundIsolate(); bindBackgroundIsolate(); return; } _port.listen((dynamic data) async { ///重新下载状态 isDown.value = false; dynamic status = data[1]; // 在这赋值进度变量 percent.value = (data[2] as int).toString(); print('data:$data'); if (status == 3) { //程序休眠1s,保证下载事项处理完成 await Future.delayed(const Duration(seconds: 1)); print('下载成功,正在打开'); await localInstallApk('$savedDir/$fileName'); // _unbindBackgroundIsolate(); } else if (status == 4) { print('下载失败'); _unbindBackgroundIsolate(); } }); // 默认进度为10间隔修改一次,可以在这加一个step: 1参数,可以隔1就回调一次 FlutterDownloader.registerCallback(Download.downloadCallback);}// 打开安装界面localInstallApk(String path) async { final res = await InstallPlugin.install(path); debugPrint("应用安装器 ${res['isSuccess'] == true ? 'success' : 'fail:${res['errorMessage'] ?? ''}'}");}/// 释放监听void _unbindBackgroundIsolate() => IsolateNameServer.removePortNameMapping(fileName);/// 注册监听事件,因为要静态方法,所以做一个类才行class Download { @pragma('vm:entry-point') static void downloadCallback( String id, int status, int progress, ) { IsolateNameServer.lookupPortByName(fileName)?.send([id, status, progress]); // print('下载任务:$id,处于状态:$status,进度为: $progress'); }} 还有两个方法,由于可能其它地方也会用到,我就做成通用方法,
你需要把这两个方法引入,或者你自己放到同一个文件
import 'dart:io';import 'package:path_provider/path_provider.dart' as path_provider;import 'package:permission_handler/permission_handler.dart';/// 查找本地文件路径并返回路径字符串/// - [path] 缓冲文件的文件名,默认就是`Download`/// - 描述:设备上没有备份的临时目录的路径,适合存放下载文件的缓存。/// - 注意:`path`参数第一位不能是`/`Future findLocalPath({String path = 'Download'}) async { final directory = Platform.isAndroid ? await path_provider.getExternalStorageDirectory() : await path_provider.getApplicationSupportDirectory(); String localPath = '${directory?.path}/$path'; final savedDir = Directory(localPath); bool hasExisted = await savedDir.exists(); if (!hasExisted) { savedDir.create(); } return localPath;}/// 检查设备存储权限并请求权限(如果未授予)Future checkPermissionStorage() async { // 获取存储权限的当前状态 var status = await Permission.storage.status; // 如果存储权限未被授予,则请求权限 if (!status.isGranted) { status = await Permission.storage.request(); // 如果权限请求被授予,返回true if (status.isGranted) { return true; } } else { // 如果权限已经授予,或者权限请求被拒绝,返回true return true; } // 如果所有条件都不符合,返回false return false;} 方法2,使用dio下载
这个我没有在上面的第三方库写dio进去,因为我觉得你会有一个http请求库的。
这个是直接下载的没有暂停的什么功能,好处就是很直接的下载
// 网络上下载apk_networkInstallApk() async { var progressValue = 0.0; var savePath = await getTemporaryDirectory('takeaway_phone_release_1.apk'); // url 就是上面的那一个 await Dio().download(url, savePath, onReceiveProgress: (count, total) { final value = count / total; // if (progressValue != value) { if (progressValue