当前位置: 首页 > news >正文

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商店&#xff08;公司内部用&#xff09;的&#xff0c;一些flutter的第三方库不合适我&#xff0c;我需要用的是从网上下载再安装&#xff08;从服务下&#xff09;&#xff0c;网上也找了花了我好几天时间。不全又乱&#xff0c;这我自己做一下备份…...

Pinctrl子系统_04_Pinctrl子系统主要数据结构

引言 本节说明Pinctrl子系统中主要的数据结构&#xff0c;对这些数据结构有所了解&#xff0c;也就是对Pinctrl子系统有所了解了。 前面说过&#xff0c;要使用Pinctrl子系统&#xff0c;就需要去配置设备树。 以内核面向对象的思想&#xff0c;设备树可以分为两部分&#x…...

设计模式(十):抽象工厂模式(创建型模式)

Abstract Factory&#xff0c;抽象工厂&#xff1a;提供一个创建一系列相关或相互依赖对 象的接口&#xff0c;而无须指定它们的具体类。 之前写过简单工厂和工厂方法模式(创建型模式)&#xff0c;这两种模式比较简单。 简单工厂模式其实不符合开闭原则&#xff0c;即对修改关闭…...

计算机网络概论01

计算机系统基础知识 基本组成 计算机系统由硬件和软件组成。 硬件由五大部分&#xff0c;他们分别是&#xff1a; 运算器 执行算数运算和逻辑运算控制器 控制cpu的工作&#xff0c;决定了计算机运行过程的自动化。包括指令控制逻辑、时序控制逻辑、总线控制逻辑和中断控制逻辑…...

新零售SaaS架构:订单履约系统架构设计(万字图文总结)

什么是订单履约系统&#xff1f; 订单履约系统用来管理从接收客户订单到将商品送达客户手中的全过程。 它连接了上游交易&#xff08;客户在销售平台下单环&#xff09;和下游仓储配送&#xff08;如库存管理、物流配送&#xff09;&#xff0c;确保信息流顺畅、操作协同&…...

Hive SQL 开发指南(三)优化及常见异常

在大数据领域&#xff0c;Hive SQL 是一种常用的查询语言&#xff0c;用于在 Hadoop上进行数据分析和处理。为了确保代码的可读性、维护性和性能&#xff0c;制定一套规范化的 Hive SQL 开发规范至关重要。本文将介绍 Hive SQL 的基础知识&#xff0c;并提供一些规范化的开发指…...

Spring Boot 自动装配的原理!!!

SpringBootApplication SpringBootConfiguration&#xff1a;标识启动类是一个IOC容器的配置类 EnableAutoConfiguration&#xff1a; AutoConfigurationPackage&#xff1a;扫描启动类所在包及子包中所有的组件&#xff0c;生…...

Linux运维_Bash脚本_编译安装Wayland-1.22.0

Linux运维_Bash脚本_编译安装Wayland-1.22.0 Bash (Bourne Again Shell) 是一个解释器&#xff0c;负责处理 Unix 系统命令行上的命令。它是由 Brian Fox 编写的免费软件&#xff0c;并于 1989 年发布的免费软件&#xff0c;作为 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 数字类型概述 数字是自然界计数活动的抽…...

每天一个数据分析题(一百九十六)

在多元线性回归模型的自变量选择方法中&#xff0c;关于向后回归法和逐步回归法的描述&#xff0c;以下哪些是正确的&#xff1f; A. 向后回归法开始时包含所有自变量&#xff0c;并逐步剔除每个不显著的变量。 B. 逐步回归法结合了向前回归法和向后回归法&#xff0c;可以在…...

华为北向网管NCE开发教程(1)闭坑选接口协议

华为北向网管NCE开发教程&#xff08;1&#xff09;闭坑选接口协议 华为北向网管NCE开发教程&#xff08;2&#xff09;REST接口开发 华为北向网管NCE开发教程&#xff08;3&#xff09;CORBA协议开发 本文一是记录自己开发华为北向网管遇到的坑&#xff0c;二是给需要的人&…...

JavaScript极速入门-综合案例(3)

综合案例 猜数字 预期效果 代码实现 <button type"button" id"reset">重新开始一局游戏</button><br>请输入要猜的数字:<input type"text" id"number"><button type"button" id"button&q…...

RabbitMQ架构详解

文章目录 概述架构详解核心组件虚拟主机&#xff08;Virtual Host&#xff09;RabbitMQ 有几种广播类型 概述 RabbitMQ是⼀个高可用的消息中间件&#xff0c;支持多种协议和集群扩展。并且支持消息持久化和镜像队列&#xff0c;适用于对消息可靠性较高的场合 官网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…...

深度学习模型部署(四)常用模型及推理平台评估指标

判断选择什么模型&#xff0c;什么量化方案&#xff0c;什么推理框架&#xff0c;最基础的知识就是如何评估自己的模型以及推理平台。 模型衡量标准 衡量一个模型的最直接标准就是运算速度&#xff0c;但是运算速度是无法计算的&#xff0c;所以定义了一些间接标准来推测模型的…...

【控制台警告】npm WARN EBADENGINE Unsupported engine

今天用webpack下载几个loader依赖&#xff0c;爆出了三个警告&#xff0c;大概的意思就是本地安装的node和npm的版本不是很匹配&#xff1f; 我的解决思路是&#xff1a; 先检查node和npm版本 然后去官网查找版本的对应 靠&#xff0c;官网404 Node.js (nodejs.org) 就找到…...

ArmSoM Rockchip系列产品 通用教程 之 GPIO 使用

1. GPIO简介​ GPIO&#xff0c;全称 General-Purpose Input/Output&#xff08;通用输入输出&#xff09;&#xff0c;是一种在计算机和嵌入式系统中常见的数字输入输出接口。它允许软件控制硬件的数字输入和输出&#xff0c;例如开关、传感器、LED灯等。GPIO通常由一个芯片或…...

npm镜像源地址

镜像源地址替换问题&#xff08;重要&#xff09; 2024 年 1 月 22 日 &#xff0c;registry.npm.taobao.org 的 SSL 证书正式过期。 2022 年 5 月 淘宝源发布了公告&#xff1a; &#xff08;大家应该没有太多关注哦&#xff0c;也包括我&#xff0c;哈哈&#xff09; &am…...

Java的Writer类详解

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…...

R语言基础的代码语法解译笔记

1、双冒号&#xff0c;即&#xff1a;“::” 要使用某个包里的函数&#xff0c;通常做法是先加载&#xff08;library&#xff09;包&#xff0c;再调用函数。最新加载的包的namespace会成为最新的enviroment&#xff0c;某些情况下可能影响函数的结果。而package name::funct…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...