Flutter APP下载更新
由于我做的项目不是放在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
package_info_plus插件,获取版本的。我这就不实现了,这个用起来没有难度的,主要是看你怎么封装对比版本,我这下面用的是网上的app连接就不用对比版本了,直接下载。
2.权限
添加权限
android\app\src\main\AndroidManifest.xml
manifest需要加上xmlns:tools="http://schemas.android.com/tools",
不然可能报错
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"><applicationandroid:label="cpm"android:name="${applicationName}"android:icon="@mipmap/launcher_icon"><!-- flutter_downloader下载器安卓配置,如果你想其它应该有权读取您的文件 --><providerandroid:name="vn.hunghd.flutterdownloader.DownloadedFileProvider"android:authorities="${applicationId}.flutter_downloader.provider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/provider_paths"/></provider><!-- 开始FlutterDownloader定制 --><!-- 禁用默认初始化器 --><providerandroid:name="androidx.startup.InitializationProvider"android:authorities="${applicationId}.androidx-startup"android:exported="false"tools:node="merge"><meta-dataandroid:name="androidx.work.WorkManagerInitializer"android:value="androidx.startup"tools:node="remove" /></provider><!-- 声明自定义初始化器 --><providerandroid:name="vn.hunghd.flutterdownloader.FlutterDownloaderInitializer"android:authorities="${applicationId}.flutter-downloader-init"android:exported="false"><!-- 更改此数字以配置最大并发任务数为5 --><meta-dataandroid:name="vn.hunghd.flutterdownloader.MAX_CONCURRENT_TASKS"android:value="5" /></provider><!-- 结束FlutterDownloader定制 --></application><!-- 允许网络连接 --><uses-permission android:name="android.permission.INTERNET" /><!-- 接入wifi状态 --><uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/><!-- 允许程序获取网络信息状态 --><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><!-- 写外部存储权限 --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><!-- 读取外部存储的权限 --><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><!-- 安装 .apk 文件(请求安装包)权限 --><uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
</manifest>
如果你是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));// 更新Appawait updateApp();runApp(const MyApp());
}
4.更新方法
前置工作
这使用get库的二次封装弹窗,这个弹窗自行实现,不然代码太多了。主要看注释下的代码
import 'package:get/get.dart';
import 'package:flutter/material.dart';
/// 更新App
updateApp() async {// 等页面加载完后再执行后面的,这个是重点,你刚进App大概是没有加载完页面的WidgetsBinding.instance.addPostFrameCallback((_) async {confirmDialog(title: '更新程序',textCancel: '稍后',textConfirm: '现在更新',isVerticalLayout: false,onCancel: () => Get.back(),// 关闭弹窗onConfirm: () async {// 下载监听bindBackgroundIsolate();// 下载await downloaderApp();// 这是另一个方法,后面讲// _networkInstallApk();},);});
}
方法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; // 文件下载ID
String savedDir = ''; // 本地文件夹路径
RxString percent = '0'.obs; // 下载进度,这我是使用Get库的状态管理准备显示在页面的// 下载文档
Future<void> 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<String> 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<bool> checkPermissionStorage() async {// 获取存储权限的当前状态var status = await Permission.storage.status;// 如果存储权限未被授予,则请求权限if (!status.isGranted) {status = await Permission.storage.request();// 如果权限请求被授予,返回trueif (status.isGranted) {return true;}} else {// 如果权限已经授予,或者权限请求被拒绝,返回truereturn true;}// 如果所有条件都不符合,返回falsereturn 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 < 1.0) {progressValue = count / total;} else {progressValue = 0.0;}debugPrint("${(progressValue * 100).toStringAsFixed(2)}%");}});final res = await InstallPlugin.install(savePath);debugPrint("install apk ${res['isSuccess'] == true ? 'success' : 'fail:${res['errorMessage'] ?? ''}'}");
}
相关文章:
Flutter APP下载更新
由于我做的项目不是放在APP商店(公司内部用)的,一些flutter的第三方库不合适我,我需要用的是从网上下载再安装(从服务下),网上也找了花了我好几天时间。不全又乱,这我自己做一下备份…...
Pinctrl子系统_04_Pinctrl子系统主要数据结构
引言 本节说明Pinctrl子系统中主要的数据结构,对这些数据结构有所了解,也就是对Pinctrl子系统有所了解了。 前面说过,要使用Pinctrl子系统,就需要去配置设备树。 以内核面向对象的思想,设备树可以分为两部分&#x…...
设计模式(十):抽象工厂模式(创建型模式)
Abstract Factory,抽象工厂:提供一个创建一系列相关或相互依赖对 象的接口,而无须指定它们的具体类。 之前写过简单工厂和工厂方法模式(创建型模式),这两种模式比较简单。 简单工厂模式其实不符合开闭原则,即对修改关闭…...
计算机网络概论01
计算机系统基础知识 基本组成 计算机系统由硬件和软件组成。 硬件由五大部分,他们分别是: 运算器 执行算数运算和逻辑运算控制器 控制cpu的工作,决定了计算机运行过程的自动化。包括指令控制逻辑、时序控制逻辑、总线控制逻辑和中断控制逻辑…...
新零售SaaS架构:订单履约系统架构设计(万字图文总结)
什么是订单履约系统? 订单履约系统用来管理从接收客户订单到将商品送达客户手中的全过程。 它连接了上游交易(客户在销售平台下单环)和下游仓储配送(如库存管理、物流配送),确保信息流顺畅、操作协同&…...
Hive SQL 开发指南(三)优化及常见异常
在大数据领域,Hive SQL 是一种常用的查询语言,用于在 Hadoop上进行数据分析和处理。为了确保代码的可读性、维护性和性能,制定一套规范化的 Hive SQL 开发规范至关重要。本文将介绍 Hive SQL 的基础知识,并提供一些规范化的开发指…...
Spring Boot 自动装配的原理!!!
SpringBootApplication SpringBootConfiguration:标识启动类是一个IOC容器的配置类 EnableAutoConfiguration: AutoConfigurationPackage:扫描启动类所在包及子包中所有的组件,生…...
Linux运维_Bash脚本_编译安装Wayland-1.22.0
Linux运维_Bash脚本_编译安装Wayland-1.22.0 Bash (Bourne Again Shell) 是一个解释器,负责处理 Unix 系统命令行上的命令。它是由 Brian Fox 编写的免费软件,并于 1989 年发布的免费软件,作为 Sh (Bourne Shell) 的替代品。 您可以在 Linu…...
Python数字类型
文章目录 Python数字类型1. 数字类型1.1 数字类型概述1.2 整数类型1.3 浮点数类型1.4 复数 2. 数字类型的操作2.1 内置的数值运算操作符2.2 内置的数值运算函数2.3 内置的数字类型转换函数 思考与练习 Python数字类型 1. 数字类型 1.1 数字类型概述 数字是自然界计数活动的抽…...
每天一个数据分析题(一百九十六)
在多元线性回归模型的自变量选择方法中,关于向后回归法和逐步回归法的描述,以下哪些是正确的? A. 向后回归法开始时包含所有自变量,并逐步剔除每个不显著的变量。 B. 逐步回归法结合了向前回归法和向后回归法,可以在…...
华为北向网管NCE开发教程(1)闭坑选接口协议
华为北向网管NCE开发教程(1)闭坑选接口协议 华为北向网管NCE开发教程(2)REST接口开发 华为北向网管NCE开发教程(3)CORBA协议开发 本文一是记录自己开发华为北向网管遇到的坑,二是给需要的人&…...
JavaScript极速入门-综合案例(3)
综合案例 猜数字 预期效果 代码实现 <button type"button" id"reset">重新开始一局游戏</button><br>请输入要猜的数字:<input type"text" id"number"><button type"button" id"button&q…...
RabbitMQ架构详解
文章目录 概述架构详解核心组件虚拟主机(Virtual Host)RabbitMQ 有几种广播类型 概述 RabbitMQ是⼀个高可用的消息中间件,支持多种协议和集群扩展。并且支持消息持久化和镜像队列,适用于对消息可靠性较高的场合 官网https://www.…...
编译内核错误 multiple definition of `yylloc‘
编译内核错误 # make ARCHarm CROSS_COMPILEarm-mix410-linux- uImageHOSTLD scripts/dtc/dtc /usr/bin/ld: scripts/dtc/dtc-parser.tab.o:(.bss0x10): multiple definition of yylloc; scripts/dtc/dtc-lexer.lex.o:(.bss0x0): first defined here collect2: error: ld ret…...
深度学习模型部署(四)常用模型及推理平台评估指标
判断选择什么模型,什么量化方案,什么推理框架,最基础的知识就是如何评估自己的模型以及推理平台。 模型衡量标准 衡量一个模型的最直接标准就是运算速度,但是运算速度是无法计算的,所以定义了一些间接标准来推测模型的…...
【控制台警告】npm WARN EBADENGINE Unsupported engine
今天用webpack下载几个loader依赖,爆出了三个警告,大概的意思就是本地安装的node和npm的版本不是很匹配? 我的解决思路是: 先检查node和npm版本 然后去官网查找版本的对应 靠,官网404 Node.js (nodejs.org) 就找到…...
ArmSoM Rockchip系列产品 通用教程 之 GPIO 使用
1. GPIO简介 GPIO,全称 General-Purpose Input/Output(通用输入输出),是一种在计算机和嵌入式系统中常见的数字输入输出接口。它允许软件控制硬件的数字输入和输出,例如开关、传感器、LED灯等。GPIO通常由一个芯片或…...
npm镜像源地址
镜像源地址替换问题(重要) 2024 年 1 月 22 日 ,registry.npm.taobao.org 的 SSL 证书正式过期。 2022 年 5 月 淘宝源发布了公告: (大家应该没有太多关注哦,也包括我,哈哈) &am…...
Java的Writer类详解
咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE相关知识点了,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好…...
R语言基础的代码语法解译笔记
1、双冒号,即:“::” 要使用某个包里的函数,通常做法是先加载(library)包,再调用函数。最新加载的包的namespace会成为最新的enviroment,某些情况下可能影响函数的结果。而package name::funct…...
如何快速掌握Snap.Hutao:Windows原神玩家的终极桌面工具箱完全指南
如何快速掌握Snap.Hutao:Windows原神玩家的终极桌面工具箱完全指南 【免费下载链接】Snap.Hutao 实用的开源多功能原神工具箱 🧰 / Multifunctional Open-Source Genshin Impact Toolkit 🧰 项目地址: https://gitcode.com/GitHub_Trending…...
Loom协程+Spring WebFlux=安全灾难?揭秘2024年生产环境爆发的4类隐蔽竞态漏洞及修复清单
第一章:Loom协程与Spring WebFlux融合演进的必然性与风险图谱随着高并发、低延迟服务需求持续攀升,传统异步非阻塞模型面临抽象层级过深、调试成本高昂、线程上下文管理复杂等结构性瓶颈。Spring WebFlux 基于 Reactor 的响应式编程范式虽显著提升了资源…...
拆解FAST-LIO2的ikd-Tree:如何用C++实现比传统方法快10倍的点云管理?
FAST-LIO2中的ikd-Tree:高性能点云管理架构深度解析 在实时SLAM系统中,点云数据的高效管理一直是制约算法性能的关键瓶颈。传统k-d树结构虽然能提供对数级别的查询效率,但在面对高频更新的点云流时,其静态特性导致的频繁重建成为性…...
独立游戏开发者的音频救星:零代码用FMOD为Unity游戏添加动态背景音乐与交互音效
独立游戏开发者的音频救星:零代码用FMOD为Unity游戏添加动态背景音乐与交互音效 当你在深夜调试游戏时,是否曾被突如其来的静默打断沉浸感?或是发现精心设计的战斗场景因为单调重复的背景音乐而失去张力?作为独立开发者ÿ…...
Stanford Doggo故障排除指南:7个核心调试技巧解决四足机器人常见问题
Stanford Doggo故障排除指南:7个核心调试技巧解决四足机器人常见问题 【免费下载链接】StanfordDoggoProject Stanford Doggo is an open source quadruped robot that jumps, flips, and trots! 项目地址: https://gitcode.com/gh_mirrors/st/StanfordDoggoProje…...
Navigation源码编译踩坑实录:从Amcl报错到完美运行的完整避坑指南
Navigation源码编译实战:从依赖解析到系统集成的深度指南 当你第一次尝试在ROS Melodic环境下从源码编译Navigation堆栈时,那种期待与忐忑交织的感觉我至今记忆犹新。作为一个长期依赖二进制包安装的开发者,转向源码编译不仅意味着对系统更深…...
游戏开发者必看:TGA文件在OpenGL/Unity/Unreal引擎中的正确打开与使用姿势
游戏开发者必看:TGA文件在OpenGL/Unity/Unreal引擎中的正确打开与使用姿势 在游戏开发的世界里,纹理贴图就像建筑师的砖瓦,而TGA格式则是其中一块被低估的金砖。不同于普通图像编辑者只需要"打开"和"查看"TGA文件&#x…...
三步彻底解决惠普OMEN游戏本性能限制:OmenSuperHub终极方案实践指南
三步彻底解决惠普OMEN游戏本性能限制:OmenSuperHub终极方案实践指南 【免费下载链接】OmenSuperHub 使用 WMI BIOS控制性能和风扇速度,自动解除DB功耗限制。 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 对于追求极致性能的惠普OM…...
保姆级教程:用NVIDIA Jetson AGX Xavier和MAX9296采集板搭建8路GMSL2相机系统
保姆级教程:用NVIDIA Jetson AGX Xavier和MAX9296采集板搭建8路GMSL2相机系统 在自动驾驶和机器人视觉系统中,多路相机同步采集是环境感知的基础。NVIDIA Jetson AGX Xavier凭借其强大的AI算力和丰富的接口资源,成为这类应用的理想平台。本文…...
Magpie v0.12.1:让Windows窗口缩放体验焕然一新的秘密武器
Magpie v0.12.1:让Windows窗口缩放体验焕然一新的秘密武器 【免费下载链接】Magpie A general-purpose window upscaler for Windows 10/11. 项目地址: https://gitcode.com/gh_mirrors/mag/Magpie 还在为Windows系统下窗口放大后画面模糊、游戏拉伸失真、办…...
