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…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
