flutter开发实战-实现多渠道打包及友盟统计(亲测有效)
flutter开发实战-实现多渠道打包及友盟统计(亲测有效)
最近开发过程中,需要引入友盟进行统计服务。友盟统计还需要区分不同渠道的打开应用的情况,所以需要处理多渠道打包的问题。
一、引入友盟统计
在工程的pubspec.yaml中引入插件
// 在工程 pubspec.yaml 中加入 dependencies:umeng_common_sdk: ^1.2.3
导入及调用初始化友盟
import 'package:umeng_common_sdk/umeng_common_sdk.dart';
调用友盟统计
@overridevoid initState() {super.initState();initPlatformState();UmengCommonSdk.initCommon('5e3f96f3cb23d2a070000048', '5e3f96f3cb23d2a070000048', 'Umeng');UmengCommonSdk.setPageCollectionModeManual();}
这里需要填写channel渠道名后续我们需要根据打包的渠道来设置。
二、flutter代码中获取渠道
Flutter命令工具增加了自定义参数的功能 --dart-define,可以用这个命令参数在打包或运行 App 时设置参数。
如:
flutter run --dart-define=CHANNEL=YYB
在lib/main.dart中定义变量配置,可以方便调用
/// 定义环境变量配置
class EnvironmentConfig {static const CHANNEL = String.fromEnvironment('CHANNEL');
}
在需要的地方调用获取渠道名
String currentChannel = "";@overridevoid initState() {// TODO: implement initStatesuper.initState();// 获取CHANNEL 参数值String channel = EnvironmentConfig.CHANNEL;print("channel:${channel}");currentChannel = channel;setState(() {});}
显示渠道名
Container(height: 136,width: 300,color: Colors.lightGreen,alignment: Alignment.center,child: Text('当前渠道:${currentChannel}',style: TextStyle(fontSize: 12, color: Colors.white),),),
最终获得渠道显示效果图如下

三、android中gradle配置
我们需要在android/app/build.gradle中添加一下配置
/// 获取渠道参数使用,这里设置一下默认值
def dartEnvironmentVariables = [CHANNEL: 'guanfang-app',
]if (project.hasProperty('dart-defines')) {dartEnvironmentVariables = dartEnvironmentVariables + project.property('dart-defines').split(',').collectEntries { entry ->def pair = new String(entry.decodeBase64(), 'UTF-8').split('=')[(pair.first()): pair.last()]}
}
在输出的apk中,添加对应渠道名,在android一下${dartEnvironmentVariables.CHANNEL}进行区分不同渠道的apk名称。
ext {publishName = 'AppDemoLab'
}android {android.applicationVariants.all {variant ->variant.outputs.all {def buildTime = new Date().format('yyyyMMddHHmm')outputFileName = "${project.publishName}_${variant.versionName}_${variant.versionCode}_${buildTime}_${variant.buildType.name}_${dartEnvironmentVariables.CHANNEL}.apk"}}
}
打包与测试命令
# 调试例子1:设置渠道为应用宝。
flutter run --dart-define=CHANNEL=YYB#打包例子1:打包应用宝渠道包
flutter build apk --dart-define=CHANNEL=YYB
可以是多个–dart-define,如:
#打包例子2:打包应用宝渠道包,DEBUG参数是Y
flutter build apk --dart-define=CHANNEL=YYB --dart-define=DEBUG=Y
四、apk.sh多渠道打包脚本
在脚本中定义了渠道channels=(YYB HUAWEI MI OPPO VIVO)
在工程目录下创建shell目录,将apk.sh放到shell目录下。
在工程目录下创建prod目录,prod目录下创建apk目录,用于存放打包的渠道apk文件
apk.sh多渠道打包脚本如下
#!/bin/sh#---------------------请修改渠道数组----------------#
channels=(YYB HUAWEI MI OPPO VIVO)#当前工程绝对路径
project_path=$(pwd)#安卓包product文件夹路径
prod_path=${project_path}/prod/apk/
#Flutter打包生成的最初地址
release_path=${project_path}/build/app/outputs/apk/release/clean_tips="执行flutter clean(默认:n) [ y/n ]"
echo $clean_tips
read -t 5 is_clean
if [ ! -n "${is_clean}" ];thenis_clean="n"
fi
while([[ $is_clean != "y" ]] && [[ $is_clean != "n" ]])
doecho "错误!只能输入[ y/n ] !!!"echo $clean_tipsread is_clean
donetips="请输入选择渠道(默认:0) [ ALL: 0 "
c_length=${#channels[@]};
for(( i=0; i<$c_length; i++)) doif (($i < $c_length-1 )); thentips="${tips}${channels[i]}: $((i+1)) "elsetips="${tips}${channels[i]}: $((i+1)) ]"fi
done;echo $tips
read -t 5 number
if [ ! -n "${number}" ];thennumber=0
fi
while(( $number < "0" || $number > $c_length ))
doecho "错误!只能输入0到${c_length} !!!"echo $tipsread number
done#如果有product/apk文件夹则删除,然后再创建一个空文件夹
if [ -d ${prod_path} ]; thenrm -rf ${prod_path}
fi
#创建目录
mkdir -p ${prod_path}if [ ${is_clean} = "y" ];thenecho "=============== 开始清理 ==============="flutter clean
fiif (($number == 0 )); thenecho "=============== 开始构建:全部渠道包 ==============="for(( i=0;i<${c_length};i++)) doecho "正在构建:${channels[$i]} 渠道包"flutter build apk --no-shrink --dart-define=CHANNEL=${channels[$i]}cp -R ${release_path}*.apk ${prod_path}done;
elseecho "=============== 正在构建:${channels[$((number-1))]} 渠道包 ==============="flutter build apk --no-shrink --dart-define=CHANNEL=${channels[$((number-1))]}cp -R ${release_path}*.apk ${prod_path}
fi#判断apk目录下是否有文件
if [ "$(ls -A $prod_path)" ]; thenecho "=============== APK包已导出:$prod_path ==============="open $prod_path
elseecho '=============== APK包导出失败 ==============='exit 1
fi
exit 0
查看脚本可以看出
控制是否执行flutter clean
输入是全部渠道打包
打包后的apk拷贝到prod/apk文件夹下。
通过cd切换到shell目录下,执行apk.sh脚本进行多渠道打包
./shell/papk.sh
在prod/apk目录下,可以看到打包的apk

参考:https://github.com/sugood/flutter_shell
五、更改友盟渠道
在文中,使用友盟时候,需要传递渠道名,我们通过EnvironmentConfig.CHANNEL拿到渠道名后作为参数传给友盟。
友盟即可根据渠道进行统计。
六、附录(完整的gradle配置)
android/build.gradle配置如下
buildscript {ext.kotlin_version = '1.7.10'repositories {maven { url "https://maven.aliyun.com/repository/google" }maven { url "https://maven.aliyun.com/repository/central" }maven { url "https://maven.aliyun.com/repository/jcenter" }}dependencies {classpath 'com.android.tools.build:gradle:7.2.0'classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"}
}allprojects {repositories {maven { url "https://maven.aliyun.com/repository/google" }maven { url "https://maven.aliyun.com/repository/central" }maven { url "https://maven.aliyun.com/repository/jcenter" }}
}rootProject.buildDir = '../build'
subprojects {project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {project.evaluationDependsOn(':app')
}task clean(type: Delete) {delete rootProject.buildDir
}
android/app/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'
}def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}/// 获取渠道参数使用,这里设置一下默认值
def dartEnvironmentVariables = [CHANNEL: 'GuanFang',
]if (project.hasProperty('dart-defines')) {dartEnvironmentVariables = dartEnvironmentVariables + project.property('dart-defines').split(',').collectEntries { entry ->def pair = new String(entry.decodeBase64(), 'UTF-8').split('=')[(pair.first()): pair.last()]}
}ext {publishName = 'AppDemoLab'
}apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"android {compileSdkVersion 34ndkVersion flutter.ndkVersioncompileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}defaultConfig {// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).applicationId "com.example.flutter_app_demolab"// You can update the following values to match your application needs.// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.minSdkVersion 21targetSdkVersion flutter.targetSdkVersionversionCode flutterVersionCode.toInteger()versionName flutterVersionNamendk {abiFilters "armeabi-v7a", "arm64-v8a"}}buildTypes {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.debug}}android.applicationVariants.all {variant ->variant.outputs.all {def buildTime = new Date().format('yyyyMMddHHmm')outputFileName = "${project.publishName}_${variant.versionName}_${variant.versionCode}_${buildTime}_${variant.buildType.name}_${dartEnvironmentVariables.CHANNEL}.apk"}}
}flutter {source '../..'
}
七、小结
flutter开发实战-实现多渠道打包及友盟统计(亲测有效),根据自身需求调整后亲测有效,可以根据渠道来做一些代码上的区分。
学习记录,每天不停进步。
本文地址:https://brucegwo.blog.csdn.net/article/details/138907985
快快微信扫码玩一玩小游戏吧
| 战机长空 | 小绳套牛 |
|---|---|
![]() | ![]() |
相关文章:
flutter开发实战-实现多渠道打包及友盟统计(亲测有效)
flutter开发实战-实现多渠道打包及友盟统计(亲测有效) 最近开发过程中,需要引入友盟进行统计服务。友盟统计还需要区分不同渠道的打开应用的情况,所以需要处理多渠道打包的问题。 一、引入友盟统计 在工程的pubspec.yaml中引入…...
JavaScript-JSON对象
JSON格式 JSON(JavaScript Object Notation, JS对象简谱)是一种轻量级的数据交换格式。它基于ECMAScript(European Computer Manufacturers Association, 欧洲计算机协会的一个子集,采用完全独立于编程语言的文本格式来存储和表示…...
【C语言】自定义类型之---结构体超详解(结构体的定义使用、指针结构体,内存对齐,......代码详解)
目录 前言: 一:结构体 1.1:什么是结构体? 1.2:结构体类型的声明 1.3:结构体变量的定义 1.4:结构体的内存对齐 1.5:结构体传参 二:位段 2.1:位段是什…...
【完美恢复】修复计算机中丢失emp.dll的多个详细方法
最近,在尝试运行某款游戏时,我遭遇了一个令人头痛的问题——“emp.dll文件丢失”。这个错误通常意味着游戏的某个关键文件没有被正确加载或已损坏。以下是我解决问题的步骤和一些心得体会,希望对遇到类似问题的玩家们有所帮助。 emp.dll是一…...
暗黑4可以搬砖吗?暗黑4怎么搬砖 搬砖攻略
暗黑4可以搬砖吗?暗黑4怎么搬砖 搬砖攻略 暗黑破坏神4属于是暴雪旗下一款经典游戏IP,在全世界有着广泛的玩家群体,更是在今年暴雪国服宣布回归之后,吸引了一大批新玩家加入。今天小编就为大家带来暗黑4的详细搬砖教程。 现在我们…...
WLAN技术
冲突域:连接在同一传输线缆上的所有工作站的集合,或者说是同一物理网段上所有节点的集合共同竞争网络资源形成的域叫冲突域。 在OSI模型中,冲突域被看作是第一层的概念,连接同一冲突域的设备有中继器、集线器(hub&…...
维修AB罗克韦尔工控机 PanelView 900 2711-T9C8 SER C 触摸屏人机界面
可视化和 HMI 解决方案可帮助您满足生产力、创新和全球化需求。为电子操作员界面终端、分布式客户端/服务器 HMI 和信息软件提供了一致的外观和感觉。编程工具和高级软件应用程序包括远程访问和数据分析,可加速开发并提高效率。 图形终端 图形终端提供各种尺寸、操…...
334_C++_std::bind中使用shared_from_this()
std::bind(&HttpClient::getPwd, shared_from_this(), std::placeholders::_1, std::placeholders::_2);[ HttpClient继承自NetObj,NetObj是父类,NetObj受到std::shared_pt...
【Python】防御性编程入门
1. 前言 防御性编程指的是为了防止代码泄露后被竞品公司窃取技术,使用一种较高级的明文加密编程方式。也可以当做一种带解密性质的时间胶囊,锻炼程序员自己的记忆能力、读代码能力等。 2. 案例分析 2.1 import Import里面可以多取一些喜欢的名字&#…...
无线麦克风哪个品牌音质最好?热门无线麦克风品牌推荐
这段时间短视频行业兴起,很多人都开始尝试步入自媒体,不过想要自己的视频内容更出色、更吸引人,好的音频设备肯定是必不可少的,而麦克风就是其中的一种。麦克风的好坏也将决定了一个视频的质量与完整性等等,如果我们作…...
粒子奇观:用Processing创造宇宙级的动态效果
前言: 👋 今天,我们将一起探索宇宙的奥秘,不是在星空下,而是在Processing的代码世界中。这是我们的第八篇文章,我们将深入粒子系统的神奇领域,学习如何创造出令人惊叹的动态效果。 粒子系统:构建动态世界的基石 🔨 粒子系统是计算机图形学中用于模拟复杂自然现象…...
Filesystem Fragmentation on Modern Storage Systems——论文泛读
TOCS 2023 Paper 论文阅读笔记整理 问题 文件系统碎片是计算机系统随着时间的推移而变慢的主要原因之一。以前认为,碎片化对硬盘驱动器(HDD)等旋转存储设备有害,但不影响固态驱动器(SSD),因为…...
如何同步管理1000个设备的VLAN数据?
什么是VLAN? VLAN,也就是虚拟局域网,是通过为子网提供数据链路连接来抽象出局域网的概念。在企业网中,一个企业级交换机一般是24口或者是48口,连接这些接口的终端在物理上形成一个广播域。广播域过大,就会导…...
【谷粒商城】01-环境准备
1.下载和安装VirtualBox 地址:https://www.virtualbox.org/wiki/Downloads 傻瓜式安装VirtualBox 2.下载和安装Vagrant官方镜像 地址:https://app.vagrantup.com/boxes/search 傻瓜式安装 验证是否安装成功 打开CMD,输入vagrant命令,是否…...
2024深圳杯数学建模C题参考论文24页+完整代码数据解题
一、问题研究 24页参考论文: 【编译器识别】2024深圳杯C题24页参考论文1-3小问完整解题代码https://www.jdmm.cc/file/2710545/ 为了回答这些问题,我们需要进行一系列的编译实验、分析编译结果,并构建判别函数。以下是对这些问题的初步分析…...
用go语言写一个代码,加班就自动给老婆发信息,下班自动提醒的代码
文章推荐 1 作为程序员,开发用过最好用的AI工具有哪些? 2 Github Copilot正版的激活成功,终于可以chat了 3 idea,pycharm等的ai assistant已成功激活 4 新手如何拿捏 Github Copilot AI助手,帮助你提高写代码效率 5 Jetbrains的a…...
Spring-Cloud 微服务
1. 微服务架构 1.1 单体应用架构---内部项目【OA WMS等】 将项目所有模块(功能)打成jar或者war,然后部署一个进程 优点: 1:部署简单:由于是完整的结构体,可以直接部署在一个服务器上即可。 2:技术单一:项目不需要复杂的技术栈,往往一套熟悉的…...
python数据分析——数据可视化(图形绘制基础)
数据可视化(图形绘制基础) 前言一、图形绘制基础Matplotlib简介使用过程sin函数示例 二、常用图形绘制折线图的绘制plot示例 散点图的绘制plot示例 柱状图的绘制bar示例 箱型图绘制plot.box示例 饼状图的绘制pie示例 三、图形绘制的组合情况多个折线图的…...
必背!!2024年软考中级——网络工程师考前冲刺几页纸
距离软考考试的时间越来越近了,趁着这两周赶紧准备起来 今天给大家整理了——软考网络工程师考前冲刺几页纸,都是核心重点,有PDF版,可打印下来,每天背一点。 计算机总线分类 ①总线的分类:数据总线、地址总…...
html+js光标操作
光标设置id为username的字段 window.addEventListener("load", function() {document.getElementById("username").focus(); }); 光标在username的时候点击enter回车键的时候光标移动到id为password的input里面 document.getElementById("username…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
FFmpeg avformat_open_input函数分析
函数内部的总体流程如下: avformat_open_input 精简后的代码如下: int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...

