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

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开发实战-实现多渠道打包及友盟统计&#xff08;亲测有效&#xff09; 最近开发过程中&#xff0c;需要引入友盟进行统计服务。友盟统计还需要区分不同渠道的打开应用的情况&#xff0c;所以需要处理多渠道打包的问题。 一、引入友盟统计 在工程的pubspec.yaml中引入…...

JavaScript-JSON对象

JSON格式 JSON&#xff08;JavaScript Object Notation, JS对象简谱&#xff09;是一种轻量级的数据交换格式。它基于ECMAScript&#xff08;European Computer Manufacturers Association, 欧洲计算机协会的一个子集&#xff0c;采用完全独立于编程语言的文本格式来存储和表示…...

【C语言】自定义类型之---结构体超详解(结构体的定义使用、指针结构体,内存对齐,......代码详解)

目录 前言&#xff1a; 一&#xff1a;结构体 1.1&#xff1a;什么是结构体&#xff1f; 1.2&#xff1a;结构体类型的声明 1.3&#xff1a;结构体变量的定义 1.4&#xff1a;结构体的内存对齐 1.5&#xff1a;结构体传参 二&#xff1a;位段 2.1&#xff1a;位段是什…...

【完美恢复】修复计算机中丢失emp.dll的多个详细方法

最近&#xff0c;在尝试运行某款游戏时&#xff0c;我遭遇了一个令人头痛的问题——“emp.dll文件丢失”。这个错误通常意味着游戏的某个关键文件没有被正确加载或已损坏。以下是我解决问题的步骤和一些心得体会&#xff0c;希望对遇到类似问题的玩家们有所帮助。 emp.dll是一…...

暗黑4可以搬砖吗?暗黑4怎么搬砖 搬砖攻略

暗黑4可以搬砖吗&#xff1f;暗黑4怎么搬砖 搬砖攻略 暗黑破坏神4属于是暴雪旗下一款经典游戏IP&#xff0c;在全世界有着广泛的玩家群体&#xff0c;更是在今年暴雪国服宣布回归之后&#xff0c;吸引了一大批新玩家加入。今天小编就为大家带来暗黑4的详细搬砖教程。 现在我们…...

WLAN技术

冲突域&#xff1a;连接在同一传输线缆上的所有工作站的集合&#xff0c;或者说是同一物理网段上所有节点的集合共同竞争网络资源形成的域叫冲突域。 在OSI模型中&#xff0c;冲突域被看作是第一层的概念&#xff0c;连接同一冲突域的设备有中继器、集线器&#xff08;hub&…...

维修AB罗克韦尔工控机 PanelView 900 2711-T9C8 SER C 触摸屏人机界面

可视化和 HMI 解决方案可帮助您满足生产力、创新和全球化需求。为电子操作员界面终端、分布式客户端/服务器 HMI 和信息软件提供了一致的外观和感觉。编程工具和高级软件应用程序包括远程访问和数据分析&#xff0c;可加速开发并提高效率。 图形终端 图形终端提供各种尺寸、操…...

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. 前言 防御性编程指的是为了防止代码泄露后被竞品公司窃取技术&#xff0c;使用一种较高级的明文加密编程方式。也可以当做一种带解密性质的时间胶囊&#xff0c;锻炼程序员自己的记忆能力、读代码能力等。 2. 案例分析 2.1 import Import里面可以多取一些喜欢的名字&#…...

无线麦克风哪个品牌音质最好?热门无线麦克风品牌推荐

这段时间短视频行业兴起&#xff0c;很多人都开始尝试步入自媒体&#xff0c;不过想要自己的视频内容更出色、更吸引人&#xff0c;好的音频设备肯定是必不可少的&#xff0c;而麦克风就是其中的一种。麦克风的好坏也将决定了一个视频的质量与完整性等等&#xff0c;如果我们作…...

粒子奇观:用Processing创造宇宙级的动态效果

前言: 👋 今天,我们将一起探索宇宙的奥秘,不是在星空下,而是在Processing的代码世界中。这是我们的第八篇文章,我们将深入粒子系统的神奇领域,学习如何创造出令人惊叹的动态效果。 粒子系统:构建动态世界的基石 🔨 粒子系统是计算机图形学中用于模拟复杂自然现象…...

Filesystem Fragmentation on Modern Storage Systems——论文泛读

TOCS 2023 Paper 论文阅读笔记整理 问题 文件系统碎片是计算机系统随着时间的推移而变慢的主要原因之一。以前认为&#xff0c;碎片化对硬盘驱动器&#xff08;HDD&#xff09;等旋转存储设备有害&#xff0c;但不影响固态驱动器&#xff08;SSD&#xff09;&#xff0c;因为…...

如何同步管理1000个设备的VLAN数据?

什么是VLAN&#xff1f; VLAN&#xff0c;也就是虚拟局域网&#xff0c;是通过为子网提供数据链路连接来抽象出局域网的概念。在企业网中&#xff0c;一个企业级交换机一般是24口或者是48口&#xff0c;连接这些接口的终端在物理上形成一个广播域。广播域过大&#xff0c;就会导…...

【谷粒商城】01-环境准备

1.下载和安装VirtualBox 地址&#xff1a;https://www.virtualbox.org/wiki/Downloads 傻瓜式安装VirtualBox 2.下载和安装Vagrant官方镜像 地址&#xff1a;https://app.vagrantup.com/boxes/search 傻瓜式安装 验证是否安装成功 打开CMD,输入vagrant命令&#xff0c;是否…...

2024深圳杯数学建模C题参考论文24页+完整代码数据解题

一、问题研究 24页参考论文&#xff1a; 【编译器识别】2024深圳杯C题24页参考论文1-3小问完整解题代码https://www.jdmm.cc/file/2710545/ 为了回答这些问题&#xff0c;我们需要进行一系列的编译实验、分析编译结果&#xff0c;并构建判别函数。以下是对这些问题的初步分析…...

用go语言写一个代码,加班就自动给老婆发信息,下班自动提醒的代码

文章推荐 1 作为程序员&#xff0c;开发用过最好用的AI工具有哪些&#xff1f; 2 Github Copilot正版的激活成功&#xff0c;终于可以chat了 3 idea,pycharm等的ai assistant已成功激活 4 新手如何拿捏 Github Copilot AI助手&#xff0c;帮助你提高写代码效率 5 Jetbrains的a…...

Spring-Cloud 微服务

1. 微服务架构 1.1 单体应用架构---内部项目【OA WMS等】 将项目所有模块(功能)打成jar或者war&#xff0c;然后部署一个进程 优点: 1:部署简单:由于是完整的结构体&#xff0c;可以直接部署在一个服务器上即可。 2:技术单一:项目不需要复杂的技术栈&#xff0c;往往一套熟悉的…...

python数据分析——数据可视化(图形绘制基础)

数据可视化&#xff08;图形绘制基础&#xff09; 前言一、图形绘制基础Matplotlib简介使用过程sin函数示例 二、常用图形绘制折线图的绘制plot示例 散点图的绘制plot示例 柱状图的绘制bar示例 箱型图绘制plot.box示例 饼状图的绘制pie示例 三、图形绘制的组合情况多个折线图的…...

必背!!2024年软考中级——网络工程师考前冲刺几页纸

距离软考考试的时间越来越近了&#xff0c;趁着这两周赶紧准备起来 今天给大家整理了——软考网络工程师考前冲刺几页纸&#xff0c;都是核心重点&#xff0c;有PDF版&#xff0c;可打印下来&#xff0c;每天背一点。 计算机总线分类 ①总线的分类&#xff1a;数据总线、地址总…...

html+js光标操作

光标设置id为username的字段 window.addEventListener("load", function() {document.getElementById("username").focus(); }); 光标在username的时候点击enter回车键的时候光标移动到id为password的input里面 document.getElementById("username…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...