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'] ?? ''}'}");
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/731352.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ODI报错

三月 08, 2024 1:20:09 下午 oracle.odi.mapping 信息: Start generation of map physical design: MapPhysicalDesign New_Mapping.物理 三月 08, 2024 1:20:09 下午 oracle.odi.mapping 信息: Finished generation of map physical design: MapPhysicalDesign New_Mapping.物…

HTML—基本介绍

HTML是一种超文本标记语言(HyperText Markup Language)&#xff0c;用于创建网页的标记语言超文本&#xff1a;是指页面内可以包含图片、链接、声音、视频等内容标记&#xff1a;HTML富含大量的标签供程序员使用&#xff0c;通过标记符号来规定指定内容的样式 浏览器最终根据不…

HybridCLR热更新介绍

官方文档 参照视频 HybridCLR介绍 HybridCLR是一个特性完整、零成本、高性能、低内存的近乎完美的Unity全平台原生c#热更方案 HybridCLR与ToLua/XLua、ILRuntime有什么不同 什么是游戏热更新&#xff1a;有热更的游戏更新流程 游戏热更新的种类 资源热更新&#xff1a;主要…

内存分区与具体介绍:

1. 内存分区图&#xff1a; 2. 分区介绍&#xff1a; 2.1. 栈区&#xff1a; 存放&#xff1a;临时的局部变量、函数的传参 特点&#xff1a;占用内存小&#xff0c;速度快 数据的存储和释放采用栈式结构&#xff0c;先入后出&#xff0c;类似于C下类的构造析构函数 2.2. 堆区&…

在Linux(Ubuntu)中使用终端编译 vscode安装

文章目录 &#x1f4da;在Linux&#xff08;Ubuntu&#xff09;中使用终端编译&#x1f407;.cpp程序编译&#x1f407;.py程序编译&#x1f407;查看Python、C编程环境 &#x1f4da;vscode安装 &#x1f4da;在Linux&#xff08;Ubuntu&#xff09;中使用终端编译 虚拟机安装…

【硬件工程师面经整理25_AD】

文章目录 1 AD设计电路全流程2 ad和cadence区别-逻辑上的区别 1 AD设计电路全流程 软件AD or 模拟数字&#xff1f; 软件AD&#xff1a;AD设计电路全流程包括以下步骤&#xff1a;选择AD库和添加、画原理图、PCB布局、PCB布线、PCB打样、PCB加工 模拟数字&#xff1a; 需求分…

实现HttpServletRequest下多次获取流数据

HttpServletRequest下多次获取流数据 背景示例错误的尝试全局替换执行顺序 背景 ​众所周知request的输入流只能读取一次&#xff0c;不能重复读取。而在HttpServletRequest中&#xff0c;获取请求体数据的流&#xff08;通过getInputStream()方法&#xff09;默认只能被读取一…

移动端精准测试之跨版本覆盖率合并

一&#xff0c;项目简介 在移动端项目测试过程中&#xff0c;尤其是发版前的回归测试阶段&#xff0c;会遇到这样的情况&#xff0c;在测试过程中测试不断地发现问题&#xff0c;开发就进行修改&#xff0c;然后打包测试。而测试完成后呢&#xff0c;业务测试同学想知道整个回归…

Qt 5.14.2 网络编程揭秘:构建高效HTTP客户端与文件下载器

引言 在当今的软件开发世界中&#xff0c;网络通信已成为不可或缺的一部分。Qt&#xff0c;作为一个跨平台的C框架&#xff0c;为我们提供了强大的网络编程能力。本文将带你深入Qt的网络模块&#xff0c;探索如何使用QNetworkAccessManager、QNetworkRequest和QNetworkReply等核…

【HarmonyOS】ArkTS-对象方法

目录 对象方法实例 对象方法 方法作用&#xff1a;描述对象的具体行为 约定方法类型 interface 接口名称 { 方法名: (参数:类型) > 返回值类型 }interface Person{dance: () > voidsing: (song: string) > void}添加方法&#xff08;箭头函数&#xff09; let ym: P…

电商数据分析18——电商广告投放的数据分析与优化

目录 写在开头1. 电商广告投放的挑战与机遇1.1 广告投放的主要目标与挑战1.2 广告数据分析的价值 2. 数据分析在广告投放中的应用2.1 目标受众分析与精准定位2.2 广告效果评估与ROI分析2.3 A/B测试与广告内容优化 3. 广告投放优化的实践案例3.1 案例分享&#xff1a;精准定向提…

springboot使用EasyExcel实现Excel导入导出

java生成Excel比较有名的框架有Apache poi、jxl等&#xff0c;但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。 EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。…

微前端之再说无界的使用

运行模式 保活模式单例模式重建模式 生命周期改造 改造入口函数&#xff1a; 应用间通信 原理props 通信 主应用可以通过 props 注入数据和方法&#xff1a;子应用可以通过 $wujie 对象来获取数据&#xff1a; window 通信 主应用调用子应用的全局数据&#xff1a;子应用调用主…

每天学习一个Linux命令之find

每天学习一个Linux命令之find 概述 在Linux系统中&#xff0c;find命令是一个非常强大的工具&#xff0c;用于查找和操作文件和目录。它可以根据一些条件来搜索指定路径下的文件&#xff0c;并执行相应的操作。本篇博客将详细介绍find命令的各个可用选项及其用法。 基本语法…

lvs集群中NAT模式

群集的含义 由多台主机构成&#xff0c;但对外表现为一个整体&#xff0c;只提供一个访问入口&#xff0c;相当于一台大型的计算机。 横向发展:放更多的服务器&#xff0c;有调度分配的问题。 垂直发展&#xff1a;升级单机的硬件设备&#xff0c;提高单个服务器自身功能。 …

如何保证消息不丢之MQ重试机制消息队列

1. 简介 死信队列&#xff0c;简称&#xff1a;DLX&#xff0c;Dead Letter Exchange&#xff08;死信交换机&#xff09;&#xff0c;当消息成为Dead message后&#xff0c;可以被重新发送到另外一个交换机&#xff0c;这个交换机就是DLX 那么什么情况下会成为Dead message&a…

波卡 Alpha 计划启动,呼唤先锋创新者重新定义 Web3 开发

原文&#xff1a;https://polkadot.network/blog/the-polkadot-alpha-program-a-new-era-for-decentralized-building-collaboration/ 编译&#xff1a;OneBlock 区块链领域不断发展&#xff0c;随之而来的是发展和创新机会的增加。而最新里程碑是令人振奋的 Polkadot Alpha …

深入理解nginx一致性哈希负载均衡模块[上]

1. 引言 在现代的网络应用中&#xff0c;负载均衡是一个至关重要的组件。它能够分配流量到多个服务器上&#xff0c;实现高可用性和性能扩展。Nginx是一个广泛使用的高性能Web服务器和反向代理服务器&#xff0c;其负载均衡模块提供了多种算法来实现流量的分发。其中&#xff0…

【刷题记录】详谈设计循环队列

下题目为个人的刷题记录&#xff0c;在本节博客中我将详细谈论设计循环队列的思路&#xff0c;并给出代码&#xff0c;有需要借鉴即可。 题目&#xff1a;LINK 循环队列是线性表吗&#xff1f;或者说循环队列是线性结构吗&#xff1f; 对于这个问题&#xff0c;我们来看一下线…

【Linux进阶之路】网络 —— “?“ (下)

文章目录 前言一、概念铺垫1.TCP2.全双工 二、网络版本计算器1. 原理简要2. 实现框架&&代码2.1 封装socket2.2 客户端与服务端2.3 封装与解包2.4 请求与响应2.5 对数据进行处理2.6 主程序逻辑 3.Json的简单使用 总结尾序 前言 在上文我们学习使用套接字的相关接口进行了…