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

Flutter - 安卓一次打包不同包名的apk

demo 地址: https://github.com/iotjin/jh_flutter_demo
代码不定时更新,请前往github查看最新代码

有时为了方便测试,同一个app需要在一个手机上装两个,直接改包名的话比较麻烦,这时可以通过添加flavor进行多维度打包,同一份代码可以设置不同的包名,app应用名称和应用图标

效果图

请添加图片描述

flutter 一键打出不同包名、应用名、版本名、签名、应用图标、版本号的安装包
Android Studio 一个工程打包多个不同包名的APK
Android Gradle —— flavorDimensions 与 productFlavors

配置

需要修改的文件有两个,一个是 android/app/build.gradle ,一个是 android/app/src/main/AndroidManifest.xml

在这里插入图片描述

productFlavors 配置

核心代码是在 build.gradle添加 productFlavors 配置

flavorDimensions("default") // 定义一个维度,这个维度的名字叫default,和下面的productFlavors中的dimension "default"对应productFlavors {// app1 | devapp1 {dimension "default"// 设置applicationId(这里很重要,两个相同applicationId的apk不能同时安装在同一台Android手机中)// applicationId "com.jh.demo1"applicationId "${defaultConfig.applicationId}.dev"// 自动生成@string/app_name为demo 把AndroidManifest.xml中的 android:label="demo"替换成 android:label="@string/app_name"// resValue "string", "app_name", "jh-demo1"// 定义app_icon字段,在AndroidManifest.xml文件中用到manifestPlaceholders = [app_name: "jh-demo1",app_icon: "@mipmap/ic_launcher",]}// app2 | prodapp2 {dimension "default"// applicationId "com.jh.demo2"applicationId "${defaultConfig.applicationId}"// resValue "string", "app_name", "jh-demo2"manifestPlaceholders = [app_name: "jh-demo2",app_icon: "@mipmap/ic_launcher",]}}

然后需要在 AndroidManifest.xmlandroid:iconandroid:label换成上面配置的app_nameapp_icon
替换前:

    <applicationandroid:name="${applicationName}"android:icon="@mipmap/ic_launcher"android:label="jh_flutter_demo">

替换后:

        <applicationandroid:name="${applicationName}"android:icon="${app_icon}"android:label="${app_name}">

新的编译和打包命令

加入flavor 后,按原来的方式调试运行或者打包会报错

编译:

 flutter run --flavor app1 -t lib/main.dart

打包:

flutter build apk --flavor app1
添加flavor后编译报错需要在 编辑器顶部 Run/Debug Configuration 里面 build flavors 设置对应的flavor 如 app1编译运行命令flutter run --flavor app1 -t lib/main.dart打包命令flutter build apk --flavor app1 --releaseflutter build apk --release --flavor app1 --target-platform=android-arm64清除build缓存并且打包app1和app2的debug和release包flutter clean; flutter build apk --flavor app1 --debug; flutter build apk --flavor app1 --release; flutter build apk --flavor app2 --debug; flutter build apk --flavor app2 --release查看包名aapt dump badging D:\apk\xxx.apk | findstr packagepackage: name='com.jh.jh_flutter_demo.dev' versionCode='6' versionName='3.16.0'aapt dump badging D:\apk\xxx.apk安裝apkadb install -r D:\apk\xxx.apk

点击run 按钮运行需要配置下图:
在这里插入图片描述
在这里插入图片描述

打包报错 可以添加下面代码

请添加图片描述

  lintOptions {//在打包Release版本的时候不进行检测checkReleaseBuilds false// 有报错也不会停止打包abortOnError false// 防止报错:Error: The resource string/app_name has not been translateddisable 'InvalidPackage'}
//    https://github.com/flutter/flutter/issues/58247
//    https://issuetracker.google.com/issues/158753935

完整build.gradle代码

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {localPropertiesFile.withReader('UTF-8') { reader ->localProperties.load(reader)}
}def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {flutterVersionCode = '1'
}def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {flutterVersionName = '1.0'
}apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('app/key/key.properties')  // 上面放置的路径
if (keystorePropertiesFile.exists()) {keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}android {compileSdkVersion 33lintOptions {disable 'InvalidPackage'}defaultConfig {// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).applicationId "com.jh.jh_flutter_demo"minSdkVersion 21targetSdkVersion 33versionCode flutterVersionCode.toInteger()versionName flutterVersionNametestInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"multiDexEnabled truendk {abiFilters  'armeabi-v7a', 'arm64-v8a', 'x86_64' // 'armeabi','x86'}}signingConfigs {release {keyAlias keystoreProperties['keyAlias']keyPassword keystoreProperties['keyPassword']storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : nullstorePassword keystoreProperties['storePassword']// v1SigningEnabled true// v2SigningEnabled true}}buildTypes {
//        debug {
//            signingConfig signingConfigs.release
//        }release {// TODO: Add your own signing config for the release build.// Signing with the debug keys for now, so `flutter run --release` works.signingConfig signingConfigs.release}}/*一次打包不同的apk添加flavor后编译报错需要在 编辑器顶部 Run/Debug Configuration 里面 build flavors 设置对应的flavor 如 app1编译运行命令flutter run --flavor app1 -t lib/main.dart打包命令flutter build apk --flavor app1 --releaseflutter build apk --release --flavor app1 --target-platform=android-arm64清除build缓存并且打包app1和app2的debug和release包flutter clean; flutter build apk --flavor app1 --debug; flutter build apk --flavor app1 --release; flutter build apk --flavor app2 --debug; flutter build apk --flavor app2 --release查看包名aapt dump badging D:\apk\xxx.apk | findstr packagepackage: name='com.jh.jh_flutter_demo.dev' versionCode='6' versionName='3.16.0'aapt dump badging D:\apk\xxx.apk安裝apkadb install -r D:\apk\xxx.apk*/flavorDimensions("default") // 定义一个维度,这个维度的名字叫default,和下面的productFlavors中的dimension "default"对应productFlavors {// app1 | devapp1 {dimension "default"// 设置applicationId(这里很重要,两个相同applicationId的apk不能同时安装在同一台Android手机中)// applicationId "com.jh.demo1"applicationId "${defaultConfig.applicationId}.dev"// 自动生成@string/app_name为demo 把AndroidManifest.xml中的 android:label="demo"替换成 android:label="@string/app_name"// resValue "string", "app_name", "jh-demo1"// 定义app_icon字段,在AndroidManifest.xml文件中用到manifestPlaceholders = [app_name: "jh-demo1",app_icon: "@mipmap/ic_launcher",]}// app2 | prodapp2 {dimension "default"// applicationId "com.jh.demo2"applicationId "${defaultConfig.applicationId}"// resValue "string", "app_name", "jh-demo2"manifestPlaceholders = [app_name: "jh-demo2",app_icon: "@mipmap/ic_launcher",]}}//    lintOptions {
//        //在打包Release版本的时候不进行检测
//        checkReleaseBuilds false
//        // 有报错也不会停止打包
//        abortOnError false
//        // 防止报错:Error: The resource string/app_name has not been translated
//        disable 'InvalidPackage'
//    }//    https://github.com/flutter/flutter/issues/58247
//    https://issuetracker.google.com/issues/158753935compileOptions {sourceCompatibility JavaVersion.VERSION_11targetCompatibility JavaVersion.VERSION_11}}flutter {source '../..'
}dependencies {testImplementation 'junit:junit:4.12'androidTestImplementation 'androidx.test:runner:1.1.1'androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • springMVC是如何做url映射到controller的?
  • HTML(六)——HTML表单和框架
  • 数据结构从入门到精通二 ~ 数组和链表
  • 一线大厂java面试题
  • go语言Gin框架的学习路线(九)
  • 构造+位运算,CF 1901C - Add, Divide and Floor
  • mac M1安装换脸Roop教程及所遇到的问题
  • 微信小程序:多图片显示及图片点击放大,多视频显示
  • git的一些使用技巧(git fetch 和 git pull的区别,git merge 和 git rebase的区别)
  • milvus的批量向量搜索
  • 数模·插值和拟合算法
  • 【Zotero插件】Zotero Tag为文献设置阅读状态 win11下相关设置
  • 上海市计算机学会竞赛平台2022年9月月赛丙组二叉树的遍历
  • 【JavaScript】 JS 的单线程和浏览器的多进程架构
  • PHP常量
  • (三)从jvm层面了解线程的启动和停止
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • docker容器内的网络抓包
  • echarts花样作死的坑
  • es6
  • golang 发送GET和POST示例
  • javascript面向对象之创建对象
  • JavaScript设计模式系列一:工厂模式
  • JavaScript异步流程控制的前世今生
  • JS专题之继承
  • PAT A1092
  • React-redux的原理以及使用
  • Sass 快速入门教程
  • 区块链共识机制优缺点对比都是什么
  • 项目管理碎碎念系列之一:干系人管理
  • 学习JavaScript数据结构与算法 — 树
  • 学习使用ExpressJS 4.0中的新Router
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • 整理一些计算机基础知识!
  • ​【已解决】npm install​卡主不动的情况
  • #如何使用 Qt 5.6 在 Android 上启用 NFC
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • (3)医疗图像处理:MRI磁共振成像-快速采集--(杨正汉)
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (zhuan) 一些RL的文献(及笔记)
  • (分类)KNN算法- 参数调优
  • (面试必看!)锁策略
  • (十六)视图变换 正交投影 透视投影
  • (贪心) LeetCode 45. 跳跃游戏 II
  • (译) 理解 Elixir 中的宏 Macro, 第四部分:深入化
  • (转载)利用webkit抓取动态网页和链接
  • .NET C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑
  • .NET CORE Aws S3 使用
  • .Net 基于MiniExcel的导入功能接口示例
  • .NET/C# 项目如何优雅地设置条件编译符号?
  • .skip() 和 .only() 的使用
  • 。Net下Windows服务程序开发疑惑
  • /etc/sudoers (root权限管理)
  • /usr/bin/env: node: No such file or directory
  • @基于大模型的旅游路线推荐方案