Flutter:打包apk,安卓版本更新(二)

在Flutter:打包apk,详细图文介绍(一)基础上,实现安卓端的版本更新功能。

1、把自己的demo文件复制到空项目中
2、生成APP图标:dart run icons_launcher:create
3、生成启动图:dart run flutter_native_splash:create
只是查看怎么在安卓端更新apk可忽略1-3步骤,这些是安装更新需要用到的依赖
# apk安装插件
app_installer: ^1.3.1
# 获取安装包路径
path_provider: ^2.1.5
# 接口请求
dio: ^5.7.0

在这里插入图片描述

pubspec.yaml

name: demo
description: "A new Flutter project."
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1environment:sdk: ^3.5.4# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:flutter:sdk: flutter# 多语言开启flutter_localizations:sdk: flutter# The following adds the Cupertino Icons font to your application.# Use with the CupertinoIcons class for iOS style icons.cupertino_icons: ^1.0.8# 状态管理get: ^4.6.6# apk安装插件app_installer: ^1.3.1# 获取安装包路径path_provider: ^2.1.5# 包信息package_info_plus: ^8.1.1# 离线存储shared_preferences: ^2.3.3# 接口请求dio: ^5.7.0# 猫哥封装基础组件,已包含配模适配ScreenUtil插件,可直接设置宽高.w,字体大小.spducafe_ui_core: ^1.0.4# 图片缓存cached_network_image: ^3.4.1# svgflutter_svg: ^2.0.16#轮播carousel_slider: ^5.0.0# uitdesign_flutter: ^0.1.7# 下拉刷新pull_to_refresh_flutter3: ^2.0.2# 加载动画flutter_easyloading: ^3.0.5# 城市选择city_pickers: ^1.3.0# 徽章badges: ^3.1.2# 主题切换adaptive_theme: ^3.6.0# 图片、视频选取extended_image: ^8.3.1#  wechat_assets_picker: ^9.3.2#  wechat_camera_picker: ^4.3.2# 图片预览photo_view: ^0.15.0# 网页#  webview_flutter: ^4.10.0# 二维码qr_flutter: ^4.1.0# 二维码扫描mobile_scanner: ^6.0.2# rename:https://pub.dev/packages/rename# 修改包名:flutter pub global run rename setBundleId --value app.demo.com# 修改程序名:flutter pub global run rename setAppName --value demorename: ^3.0.2dev_dependencies:flutter_test:sdk: flutter# 启动屏flutter_native_splash: ^2.4.1# 启动图标# 图标设计:https://www.canva.com/logos/templates/# 图标工具:https://icon.kitchen/# 生成APP图标执行:dart run icons_launcher:createicons_launcher: ^3.0.0flutter_lints: ^4.0.0# app 图标
icons_launcher:# 默认图标的路径image_path: "assets/icons/ic_logo.png"platforms:android:enable: true# 消息图片,手机顶部状态栏弹出消息时notification_image: "assets/icons/ic_foreground.png"# adaptive_background_color: '#ffffff'# 图标背景色adaptive_background_image: "assets/icons/ic_background.png"# 图标前景色(透明背景+图标)adaptive_foreground_image: "assets/icons/ic_foreground.png"ios:enable: true# 启动图适配 android 11 及以下, 12 以上,IOS
# 生成:dart run flutter_native_splash:create
# 删除:dart run flutter_native_splash:remove
flutter_native_splash:web: falsecolor_android: "#ffffff"# background_image_android: "assets/launcher/background.png"background_image_ios: "assets/launcher/background.png"# image_ios: "assets/launcher/android.png"android_12:image: "assets/launcher/android12.png"# icon_background_color: "#324ea1"flutter:uses-material-design: trueassets:- assets/images/- assets/svgs/- assets/styleWidget/- assets/img/fonts:- family: Montserratfonts:- asset: assets/fonts/Montserrat/Montserrat-Light.ttfweight: 300- asset: assets/fonts/Montserrat/Montserrat-Regular.ttfweight: 400- asset: assets/fonts/Montserrat/Montserrat-Medium.ttfweight: 500- asset: assets/fonts/Montserrat/Montserrat-Bold.ttfweight: 700

一、配置权限android/app/src/main/AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"><!-- 权限声明部分 --><uses-permission android:name="android.permission.INTERNET"/> <!-- 允许应用访问网络,用于下载APK --><uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> <!-- 允许应用请求安装APK--><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 允许应用写入外部存储 --><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <!-- 允许应用读取外部存储 --><applicationandroid:label="zhongmuyun"android:name="${applicationName}"android:icon="@mipmap/ic_launcher"><!-- FileProvider配置 --><providerandroid:name="androidx.core.content.FileProvider"android:authorities="${applicationId}.fileProvider"android:exported="false"android:grantUriPermissions="true"><!-- FileProvider是Android 7.0后推出的文件访问机制,用于安全地分享文件 --><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths" /> <!-- 指定可访问路径的配置文件 --></provider><activity>.........</activity></application>
</manifest>

二、创建file_paths.xml,android/app/src/main/res/xml/file_paths.xml
如果没有xml,就创建个xml文件夹

<?xml version="1.0" encoding="utf-8"?>
<paths><external-path name="external_files" path="."/><cache-path name="cache" path="."/><external-cache-path name="external_cache" path="."/>
</paths>

三、components封装

在这里插入图片描述

version_update_dialog.dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:demo/common/index.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import 'package:ducafe_ui_core/ducafe_ui_core.dart';class VersionUpdateDialog extends StatelessWidget {final String? version;final String? description;final String? apkUrl;final VoidCallback? onCancel;final RxBool isDownloading;final RxDouble downloadProgress;final Function() onUpdate;const VersionUpdateDialog({Key? key,this.version,this.description,this.apkUrl,this.onCancel,required this.isDownloading,required this.downloadProgress,required this.onUpdate,}) : super(key: key);@overrideWidget build(BuildContext context) {return Center(child: TDPopupCenterPanel(closeUnderBottom: true,closeClick: () {onCancel?.call();Get.back();},child: SizedBox(width: 590.w,height: 680.w,child: <Widget>[TDImage(assetUrl: 'assets/img/update.png',width: 590.w,height: 280.w,fit: BoxFit.contain,),SizedBox(height: 20.w),const TextWidget.body('发现新版本',textAlign: TextAlign.center,),SizedBox(height: 10.w),TextWidget.body(version ?? '',textAlign: TextAlign.center,),SizedBox(height: 10.w),<Widget>[TextWidget.body(description ?? '',size: 24.sp,maxLines: 2,overflow: TextOverflow.ellipsis,textAlign: TextAlign.center,).width(460.w),].toRow(mainAxisAlignment: MainAxisAlignment.center,).paddingAll(30.w).card(color: Color(0xffF6F7F9)).width(530.w),SizedBox(height: 30.w),Obx(() => isDownloading.value ? <Widget>[TextWidget.body('下载中...${downloadProgress.value.toInt()}%',color: Colors.white,textAlign: TextAlign.center,),].toRow(mainAxisAlignment: MainAxisAlignment.center,).card(color: Colors.blue).tight(width: 530.w,height: 88.w): TDButton(text: '立即更新',isBlock: true,width: 530.w,height: 88.w,margin: const EdgeInsets.all(0),style: TDButtonStyle(backgroundColor: Colors.blue,textColor: Colors.white,radius: BorderRadius.circular(20.w),),onTap: onUpdate,),),].toColumn(crossAxisAlignment: CrossAxisAlignment.center,),),),);}
}

version_update_utils.dart

import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
import 'package:app_installer/app_installer.dart';
import 'package:demo/common/index.dart';
import 'version_update_dialog.dart';class VersionUpdateUtil {static final RxDouble downloadProgress = 0.0.obs;static final RxBool isDownloading = false.obs;// 检查并显示更新static void checkUpdate({required String currentVersion,required String latestVersion,required String description,required String apkUrl,}) {if (_shouldUpdate(currentVersion, latestVersion)) {_showUpdateDialog(version: latestVersion,description: description,apkUrl: apkUrl,);}}// 显示更新弹窗static void _showUpdateDialog({required String version,required String description,required String apkUrl,}) {Get.dialog(VersionUpdateDialog(version: version,description: description,apkUrl: apkUrl,isDownloading: isDownloading,downloadProgress: downloadProgress,onUpdate: () => _downloadAndInstallApk(apkUrl),),barrierDismissible: false,barrierColor: Get.theme.dividerColor.withOpacity(0.5),transitionDuration: const Duration(milliseconds: 200),transitionCurve: Curves.easeInOut,useSafeArea: true,);}// 下载并安装APKstatic Future<void> _downloadAndInstallApk(String apkUrl) async {if (isDownloading.value) return;try {isDownloading.value = true;final dir = await getExternalStorageDirectory();if (dir == null) {Loading.error('无法获取存储目录');return;}final apkPath = '${dir.path}/app-update.apk';await Dio().download(apkUrl,apkPath,onReceiveProgress: (received, total) {if (total != -1) {downloadProgress.value = ((received / total) * 100).roundToDouble();}},);if (Platform.isAndroid) {await AppInstaller.installApk(apkPath);} else {Loading.error('仅支持Android设备');}isDownloading.value = false;Get.back();} catch (e) {isDownloading.value = false;downloadProgress.value = 0;Loading.error('下载失败:$e');}}// 比较版本号static bool _shouldUpdate(String currentVersion, String latestVersion) {List<int> current = currentVersion.split('.').map((e) => int.parse(e)).toList();List<int> latest = latestVersion.split('.').map((e) => int.parse(e)).toList();for (int i = 0; i < current.length && i < latest.length; i++) {if (latest[i] > current[i]) return true;if (latest[i] < current[i]) return false;}return latest.length > current.length;}
}

测试一下:

_initData() async {// 接口拿到更新数据versionUpdateModel = await SystemApi.versionUpdate();// 使用工具类检查更新,为了方便展示,把更新数据写死测试安装VersionUpdateUtil.checkUpdate(currentVersion: '1.0.0',latestVersion: '1.0.1',description: '更新内容',apkUrl: 'http://oss.***/files/23b16eaa75eb942d12e9bdb0cabae8b1.apk',// apkUrl: versionUpdateModel?.akpUrl ?? '',);update(["version_update"]);
}

在这里插入图片描述
点击更新后,下载会计算下载进度。
在这里插入图片描述

在这里插入图片描述

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

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

相关文章

execl条件比较两个sheet每个单元格的值

1.把对比的sheet复制到对比文件中 2.选择首个单元格 3.新建规则 4.选择公式 5.编写公式 A3<>Sheet1!A36.选择差异颜色 7.选择应用范围 $1:$655368.选择应用范围

FPGA的 基本结构(Xilinx 公司Virtex-II 系列FPGA )

以Xilinx 公司Virtex-II 系列FPGA 为例&#xff0c;其基本结构由下图所示。它是主要由两大部分组成&#xff1a;可编程输入/输出&#xff08;Programmable I/Os&#xff09;部分和内部可配置&#xff08;Configurable Logic&#xff09;部分。 可编程输入/输出&#xff08;I/Os…

HarmonyOS鸿蒙开发 弹窗及加载中指示器HUD功能实现

HarmonyOS鸿蒙开发 弹窗及加载中指示器HUD功能实现 最近在学习鸿蒙开发过程中&#xff0c;阅读了官方文档&#xff0c;在之前做flutter时候&#xff0c;经常使用overlay&#xff0c;使用OverlayEntry加入到overlayState来做添加悬浮按钮、提示弹窗、加载中指示器、加载失败的t…

【蓝桥杯选拔赛真题60】C++寻宝石 第十四届蓝桥杯青少年创意编程大赛 算法思维 C++编程选拔赛真题解

目录 C++寻宝石 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 五、运行结果 六、考点分析 七、推荐资料 C++寻宝石 第十四届蓝桥杯青少年创意编程大赛C++选拔赛真题 一、题目要求 1、编程实现 有N(1<N<100)个盒子排成一排,每个盒子都放…

ue5 蒙太奇,即上半身动画和下半身组合在一起,并使用。学习b站库得科技

本文核心 正常跑步动画端枪动画跑起来也端枪 正常跑步动画 端枪动画的上半身 跑起来也端枪 三步走&#xff1a; 第一步制作动画蒙太奇和插槽 第二步动画蓝图选择使用上半身动画还是全身动画&#xff0c;将上半身端枪和下半身走路结合 第三步使用动画蒙太奇 1.开始把&a…

2025年01月09日Github流行趋势

1. 项目名称&#xff1a;khoj 项目地址url&#xff1a;https://github.com/khoj-ai/khoj项目语言&#xff1a;Python历史star数&#xff1a;22750今日star数&#xff1a;1272项目维护者&#xff1a;debanjum, sabaimran, MythicalCow, aam-at, eltociear项目简介&#xff1a;你…

Idea-离线安装SonarLint插件地址

地址&#xff1a; SonarQube for IDE - IntelliJ IDEs Plugin | Marketplace 选择Install Plugin from Disk..&#xff0c;选中下载好的插件&#xff0c;然后重启idea

MT6706BL 同步整流 规格书

MT6706BL 是用于反激式变换器的高性能 65V 同步整流器。MT6706BL兼容各种反激转换器类型。MT6706BL 支持 DCM、CCM 和准谐振模式。MT6706BL 集 成 了 一 个 65V 功 率MOSFET&#xff0c;可以取代肖特基二极管&#xff0c;提高效率。V SW <V TH-ON 时&#xff0c;MT6706BL 内…

linux centos挂载未分配的磁盘空间

使用到的命令 lshw -class disk -short hostnamectl fdisk /dev/sdb partprobe /dev/sdb mount /dev/sdb2 /opt/fastdfs/ mkfs.ext4 /dev/sdb2 mount -t ext4 /dev/sdb2 /opt/fastdfs/

在 macOS 中,设置自动将文件夹排在最前

文章目录 1、第一步访达设置2、第二步排序方式 需要两步设置 1、第一步访达设置 按名称排序的窗口中 2、第二步排序方式 选择名称

【LeetCode Hot100 贪心算法】 买卖股票的最佳时机、跳跃游戏、划分字母区间

贪心算法 买卖股票的最佳时机买卖股票的最佳时机II跳跃游戏跳跃游戏II划分字母区间 买卖股票的最佳时机 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的…

人工智能-机器学习之多元线性回归(项目实践一)

目标&#xff1a;运用scikit-learn进行多元线性回归方程的构建&#xff0c;通过实际案例的训练集和测试集进行预测&#xff0c;最终通过预测结果和MSE来评估预测的精度。 一、首先安装scikit-learn&#xff1a;pip install scikit-learn C:\Users\CMCC\PycharmProjects\AiPro…

MySql根据经纬度查询距离

一、搭建测试 创建数据表() CREATE TABLE sys_test (id int(11) NOT NULL AUTO_INCREMENT COMMENT 主键ID,name varchar(20) DEFAULT NULL COMMENT 名称,longitude decimal(10,6) DEFAULT NULL COMMENT 经度,latitude decimal(10,6) DEFAULT NULL COMMENT 维度,PRIMARY KEY (id…

api开发如何在代码中使用京东商品详情接口的参数?

选择编程语言和相关工具 以 Python 为例&#xff0c;你可以使用requests库来发送 HTTP 请求获取接口数据。如果是 Java&#xff0c;可以使用OkHttp等库。 Python 示例 假设你已经安装了requests库&#xff0c;以下是一个简单的代码示例来获取和使用京东商品详情接口参数&#…

【可实战】Bug的判定标准、分类、优先级、定位方法、提交Bug(包含常见面试题)

一、Bug相关概念 &#xff08;一&#xff09;bug判定标准 &#xff08;二&#xff09;常见 Bug 分类 &#xff08;三&#xff09;bug优先级 1.bug严重程度与优先级的关系 有些很严重的Bug&#xff0c;只在极端的条件下才出现&#xff0c;用户碰到的概率很低&#xff0c;这种情…

SpringBoot之核心配置

学习目标&#xff1a; 1.熟悉Spring Boot全局配置文件的使用 2.掌握Spring Boot配置文件属性值注入 3.熟悉Spring Boot自定义配置 4.掌握Profile多环境配置 5.了解随机值设置以及参数间引用 1.全局配置文件 Spring Boot使用 application.properties 或者application.yaml 的文…

GitLab创建用户,设置访问SSH Key

继上一篇 Linux Red Hat 7.9 Server安装GitLab-CSDN博客 安装好gitlab&#xff0c;启用管理员root账号后&#xff0c;开始创建用户账户 1、创建用户账户 进入管理后台页面 点击 New User 输入用户名、邮箱等必填信息和登录密码 密码最小的8位&#xff0c;不然会不通过 拉到…

1688平台商品关键词搜索的多样性与Python爬虫应用实践

在当今这个信息化、数字化飞速发展的时代&#xff0c;电子商务平台已经成为人们日常生活中不可或缺的一部分。而1688作为国内知名的B2B电商平台&#xff0c;凭借其庞大的商品种类和丰富的供应链资源&#xff0c;为无数商家和消费者提供了便捷的交易渠道。除了广受关注的女装品类…

为深度学习引入张量

为深度学习引入张量 什么是张量&#xff1f; 神经网络中的输入、输出和转换都是使用张量表示的&#xff0c;因此&#xff0c;神经网络编程大量使用张量。 张量是神经网络使用的主要数据结构。 张量的概念是其他更具体概念的数学概括。让我们看看一些张量的具体实例。 张量…

点击底部的 tabBar 属于 wx.switchTab 跳转方式,目标页面的 onLoad 不会触发(除非是第一次加载)

文章目录 1. tabBar 的跳转方式2. tabBar 跳转的特点3. 你的配置分析4. 生命周期触发情况5. 总结 很多人不明白什么是第一次加载&#xff0c;两种情况讨论&#xff0c;第一种情况假设我是开发者&#xff0c;第一次加载就是指点击微信开发者工具上边的编译按钮&#xff0c;每点击…