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…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...
Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...
Redis上篇--知识点总结
Redis上篇–解析 本文大部分知识整理自网上,在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库,Redis 的键值对中的 key 就是字符串对象,而 val…...

