Flutter集成文档
介绍
Umeng Flutter APM SDK 能够全面监控Flutter端线上稳定性和性能的运行情况,洞悉设备运行体感。目前 SDK提供了监控dart异常、页面性能、页面帧率等能力,并Flutter Boost插件集成 支持版本v2.2.0
⚠️ 注意:
支持iOS和Android平台
SDK 支持Flutter官方提供的Flutter SDK运行环境。
欢迎开发者加入Flutter APM开发者交流群

SDK集成简易架构图

Flutter SDK 运行搭配最低版本下限
|
|
|
Flutter APM和Flutter Common SDK内部已集成了Android和iOS SDK,如果工程类型是Flutter App可以直接跳到步骤三,直接进入Flutter SDK集成步骤
步骤一、申请AppKey创建应用
⚠️ 注意:
如需在已创建的平台(iOS,Android)应用中集成Flutter SDK可忽略此步骤
1.1、进入APM后台添加应用

1.2、新建应用

1.3、获取Appkey

步骤二、合规声明和初始化时机
为了满足监管的规定通常我们需要在APP中通过《隐私政策》中向用户告知使用友盟SDK。我们可以根据《隐私政策》弹窗或者页面所在架构层来判断调用所在端的Common SDK 初始化方法,因为我们需要在用户同意操作的回调中调用初始化Init Common SDK的方法。
2.1 《隐私政策》写在Native端参考如下:
2.2 《隐私政策》写在Flutter端参考如下:
注意:我们提供了可以桥接Native Common SDK能力的Flutter Common SDK,即使《隐私政策》在Flutter端也需要必须先集成Native Common SDK。
import 'package:umeng_common_sdk/umeng_common_sdk.dart';
class _MyAppState extends State {
@override
void initState() {
super.initState();
// 如合规声明在Flutter端,请在同意操作回调中添加下列调用方法
UmengCommonSdk.initCommon(
'androidAppkey', 'iosAppkey', 'Umeng');
UmengCommonSdk.setPageCollectionModeManual();
}
}Flutter Common SDK集成参考:https://developer.umeng.com/docs/119267/detail/174923
合规声明文档参考:https://developer.umeng.com/docs/193624/detail/194588
步骤三、对接 Flutter SDK
APM SDK支持工程Flutter版本范围
flutter: ">=2.0.0" // 适用于Flutter SDK 2.0以上版本3.1 添加SDK
3.1.1 手动集成
在【友盟+】官网下载,选取Flutter【应用性能监控】SDK进行下载。下载地址

将下载后的SDK文件夹放进工程中,在pubspec.yaml中利用相对路径进行引用
name: umeng_apm_sdk_example
description: Demonstrates how to use the umeng_apm_sdk plugin.
version: 1.0.0
publish_to: 'none'
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=2.0.0"
dependencies:
flutter:
sdk: flutter
umeng_common_sdk: ^1.2.6
umeng_apm_sdk:
path: '<sdk file>'3.1.2 自动集成
打开 pubspec.yaml,添加以下依赖:
dependencies:
umeng_apm_sdk: ^2.2.1
umeng_common_sdk: ^1.2.4
在安装umeng_apm_sdk的时候,如果出现报错,报错示例如下图,提示http包与其他SDK的http包的版本产生冲突,这个时候需要在pubspec.yaml下通过dependency_overrides锁定一下http包的版本,因为http 1.x.x 配套是dart 3.0 其中还有很多工程环境使用的是低于dart 3.0的版本 所以我们把http的依赖到0.13.1的版本 对应到dart 2.12这个支持空安全的里程碑版本,这样umeng_apm_sdk向下兼容。

// 示例
dependencies:
umeng_apm_sdk: ^2.2.1
umeng_common_sdk: ^1.2.4
dependency_overrides
http: ^x.x.x3.2 初始化 SDK 探针
字段 | 含义 | 是否必填 | 类型 | 获取方式 |
name | 应用或模块名称 | 是 | string | pubspec.yaml => name |
bver | 应用或模块版本(+构建号) | 是 | string | pubspec.yaml => version |
projectType | 工程类型 (默认为0) Flutter App = 0 Flutter Module = 1 | 否 | int | |
env | 设置业务环境 v2.2.0 版本及以上可支持 | 否 | UmengApmEnv |
|
flutterVersion | Flutter SDK 版本 | 否 | string | flutter --version
|
engineVersion | 引擎版本 | 否 | string | |
enableLog | 是否开启SDK日志打印 (默认关闭) | 否 | bool | |
enableTrackingPageFps | 开启监测页面帧率(默认关闭) v2.1.3 版本及以上可支持 | 否 | bool | |
enableTrackingPagePerf | 开启监测页面性能(默认关闭) v2.1.3 版本及以上可支持 | 否 | bool | |
errorFilter | 设置采集的异常黑白名单 | 否 | Map | |
initFlutterBinding | ApmWidgetsFlutterBinding的覆写和初始化方法 | 否 | Function | |
onError | 抛出异常回调 | 否 | Function |
⚠️ 注意:
确保 bver 精确到构建号,可以使后台符号表解析能够映射到指定版本
3.2.1 初始化 UmengApmSdk
为保证能够捕获全局的异常,我们建议您将应用的 void main() => runApp(MyApp());替换成以下代码
import 'package:umeng_apm_sdk/umeng_apm_sdk.dart';
import 'package:umeng_common_sdk/umeng_common_sdk.dart';
import 'package:package_info_plus/package_info_plus.dart';
void main() {
final UmengApmSdk umengApmSdk = UmengApmSdk(
name: '应用或者模块名称',
bver: '您的Flutter应用或模块版本 (比如 1.0.0+1 )',
// 是否开启SDK运行时日志输出
enableLog: true,
// 您使用的flutter版本,默认为空,为方便定位访问,建议配置
flutterVersion: '您使用的flutter版本',
engineVersion: '您使用的flutter引擎版本',
// 开启监测页面帧率(默认关闭) 版本 v2.1.3 可支持
enableTrackingPageFps: true,
// 开启监测页面性能(默认关闭)版本 v2.1.3 可支持
enableTrackingPagePerf: true,
// 带入继承ApmWidgetsFlutterBinding的覆写和初始化方法, 可用于自定义监听应用生命周期
// 确保去掉原有的WidgetsFlutterBinding.ensureInitialized() ,以免出现重复初始化绑定的异常造成无法正常初始化,SDK内部已通过initFlutterBinding入参带入继承的WidgetsFlutterBinding实现初始化操作
initFlutterBinding: MyApmWidgetsFlutterBinding.ensureInitialized,
// 抛出异常事件
onError: (exception, stack) {
print(exception);
},
);
umengApmSdk.init(appRunner: (observer) async {
// 确保去掉原有的WidgetsFlutterBinding.ensureInitialized() ,以免出现重复初始化绑定的异常造成无法正常初始化,SDK内部已通过initFlutterBinding入参带入继承的WidgetsFlutterBinding实现初始化操作
// 依赖ensureInitialized()初始化的代码可在此调用
// 需要异步获取设置应用名称和版本号可在此回调中操作
// SDK实例化的设置可先将name和bver 为 "",然后通过以下方式进行设置
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String packageName = packageInfo.packageName;
String buildNumber = packageInfo.buildNumber;
String version = packageInfo.version;
umengApmSdk.name = packageName;
umengApmSdk.bver = '$version+$buildNumber';
return MyApp(observer);
});
}
// 如果使用Flutter Boost插件需要通过 with 混入了 Boost提供的BoostFlutterBinding 类
// class MyApmWidgetsFlutterBinding extends ApmWidgetsFlutterBinding with BoostFlutterBinding {}
// 具体参考下方3.2.2的部分
class MyApmWidgetsFlutterBinding extends ApmWidgetsFlutterBinding {
@override
void handleAppLifecycleStateChanged(AppLifecycleState state) {
// 添加自己的实现逻辑
print('AppLifecycleState changed to $state');
super.handleAppLifecycleStateChanged(state);
}
static WidgetsBinding? ensureInitialized() {
MyApmWidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
class MyApp extends StatelessWidget {
MyApp([this._navigatorObserver]);
NavigatorObserver? _navigatorObserver;
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
routes: routes,
initialRoute: "/",
navigatorObservers: <NavigatorObserver>[
// 带入ApmNavigatorObserver实例用于路由监听
_navigatorObserver ?? ApmNavigatorObserver.singleInstance
],
);
}
}
⚠️ 注意:
确保去掉原有的WidgetsFlutterBinding.ensureInitialized() ,以免出现重复初始化绑定的异常造成无法正常初始化,SDK内部已通过
initFlutterBinding入参带入继承的WidgetsFlutterBinding实现初始化操作
3.2.2 注册Flutter Boost插件监听器
支持 Flutter Boost插件(v2.2.0及以上版本支持)
3.2.3 注册监听器(非Flutter Boost插件环境使用)
我们需要在 MyApp中注册监听器,将ApmNavigatorObserver.singleInstance添加到应用的navigatorObservers 列表中
UmengApmSdk(
name: '应用或者模块名称',
......
).init(appRunner: (observer)
// 入参 ApmNavigatorObserver 实例
return MyApp(observer);
});
}
class MyApp extends StatelessWidget {
MyApp([this._navigatorObserver]);
NavigatorObserver? _navigatorObserver;
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
routes: routes,
initialRoute: "/",
navigatorObservers: <NavigatorObserver>[
// 带入ApmNavigatorObserver实例用于路由监听
_navigatorObserver ?? ApmNavigatorObserver.singleInstance
],
);
}
}
⚠️ 注意:
如果不带入SDK监听器将无法获知页面(PV)入栈退栈行为,
错误率(Dart异常数/FlutterPV次数)将异常攀升。
3.2.4 监听滚动FPS(v2.1.3 及以上版本支持)
使用SDK ApmScrollController实例,注册滚动控制器用于监听滚动事件。
import 'package:flutter/material.dart';
// 添加SDK
import 'package:umeng_apm_sdk/umeng_apm_sdk.dart';
class ScrollLazyLoadPage extends StatefulWidget {
@override
_ScrollLazyLoadPageState createState() => _ScrollLazyLoadPageState();
}
class _ScrollLazyLoadPageState extends State<ScrollLazyLoadPage> {
List<String> imageUrls = [];
int page = 1;
// 使用APM滚动控制器(ApmScrollController)
ScrollController _scrollController = ApmScrollController();
bool isLoading = false;
@override
void initState() {
super.initState();
fetchData();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
fetchData();
}
});
}
Future<void> fetchData() async {
if (!isLoading) {
setState(() {
isLoading = true;
});
// Simulating a delay of 2 seconds
await Future.delayed(Duration(seconds: 2));
final List<String> urls = [
'https://www.example.cn/community/01jz5vsgfkgclicuhmruvn3633.jpg',
.......
];
try {
setState(() {
imageUrls.addAll(urls);
isLoading = false;
});
} catch (e) {}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Scroll Lazy Load Demo'),
),
body: GridView.builder(
controller: _scrollController,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemCount: imageUrls.length + 1,
itemBuilder: (context, index) {
if (index == imageUrls.length) {
return Center(
child:
isLoading ? CircularProgressIndicator() : SizedBox.shrink(),
);
}
return Card(
child: Image.network(
imageUrls[index],
fit: BoxFit.cover,
),
);
},
),
);
}
}
3.2.5 自定义异常
captureException(类型:Function)
入参配置 | 含义 | 是否必传 | 类型 |
exception | 异常摘要 | 是 | Exception |
stack | 异常堆栈 | 否 | String |
extra | 自定义属性 | 否 | Map<String, dynamic> |
案例一
import 'package:umeng_apm_sdk/umeng_apm_sdk.dart';
void main() {
Isolate isolate = await Isolate.spawn(runIsolate, []);
// 监听isolate异常
isolate.addErrorListener(RawReceivePort((pair) {
var error = pair[0];
var stacktrace = pair[1];
// 主动采集isolate异常
ExceptionTrace.captureException(
exception: Exception(error),
stack: stacktrace.toString());
}).sendPort);
}
案例二
import 'package:umeng_apm_sdk/umeng_apm_sdk.dart';
void main() {
try {
List<String> numList = ['1', '2'];
print(numList[5]);
} catch (e) {
// 主动捕获上报代码执行异常
ExceptionTrace.captureException(
exception: Exception(e), extra: {"user": '123'});
}
}
3.2.5 黑白名单设置 ErrorFilter
用于设置采集的项的黑白名单,可以在黑名单和白名单中选择其一,如果选择白名单的方式,那么只有符合标准的页面会被采集,如果选择的是黑名单的方式,那么符合标准的页面不会被采集
此项非必须参数,用于判断是否过滤日志,包含如下属性
属性 | 含义 | 默认 | 类型 |
mode | 匹配模式 当值为ignore,表示黑名单模式,命中规则的不上报 当值为match,表示白名单模式命中规则的上报 | ignore | 枚举值 ignore|match |
rules | 匹配规则集合,当类型为数组时,表示规则集合,规则之间为或的关系,只要任意一个规则命中,则规则集命中。 | [],该默认值表示黑名单为空,日志全部上报 | Array<string | RegExp > |
void main() {
UmengApmSdk(
name: '应用或者模块名称',
// 过滤异常筛选
errorFilter: {
"mode": "match",
"rules": [RegExp('RangeError')],
}
....
).init(appRunner: (observer)
return MyApp(observer);
});
}步骤四、Native SDK 配置
注意:
集成Flutter SDK(已内置原生APM和Common SDK依赖)不需要在原生项目中引入原生SDK,如果您的原生项目中的Cocoapods或手动集成依赖库内存在友盟SDK依赖(原生UMCommon、原生UMAPM),需要删掉,否则可能会导致SDK冲突。
// Podfile
target 'UMPlusDemo'do
// pod 'UMCommon'
// pod 'UMAPM'
// pod 'UMDevice'
end4.1 iOS
4.1.1 初始化并开启原生采集开关配置项
重点关注:如果您还想采集Native 崩溃、ANR等日志可以参考下面设置
如下代码如需支持Swift 配置参考如下文档:https://developer.umeng.com/docs/193624/detail/194595#p-9wu-kzn-of8
U-APM iOS采集开关配置项(UMAPMConfig)说明请参考此文档:https://developer.umeng.com/docs/193624/detail/291394#h3-v47-jej-ofq
/** 初始化友盟所有组件产品接口函数
@param appKey 开发者在友盟官网申请的appkey.
@param channel 渠道标识,可设置nil表示"App Store".
*/
+(void)initWithAppkey:(NSString*)appkey channel:(NSString*)channel;
/************ 开始集成 ************/
// 在.m文件中加入如下代码
#import <UMCommon/UMCommon.h>
#import <UMAPM/UMCrashConfigure.h>
#import <UMAPM/UMLaunch.h>
#import <UMAPM/UMAPMConfig.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//在初始化appkey前调用,防止iOS13及以下同时使用NSURLProtocol和U-APM网络模块冲突。(未使用NSURLProtocol或者先初始化NSURLProtocol,可不调用此函数)
//[UMCrashConfigure enableNetworkForProtocol:NO];
UMAPMConfig* config = [UMAPMConfig defaultConfig];
config.crashAndBlockMonitorEnable = YES;
config.launchMonitorEnable = YES;
config.memMonitorEnable = NO;
config.oomMonitorEnable = NO;
config.networkEnable = YES;
[UMCrashConfigure setAPMConfig:config];//必须配置,请注意
// flutter_module调用及同意隐私政策代码...
// 务必要在同意隐私政策后初始化
[UMConfigure initWithAppkey:@"这里填写您的Appkey" channel:@"App Store"];
return YES;
}⚠️ 注意
已集成过iOS APM SDK的应用,请在工程环境下执行
pod update确保SDK 版本更新到1.8.4及以上
4.2 Android
注意:集成Flutter SDK不需要在原生项目中引入原生SDK,如果您的原生项目中的build.gradle存在友盟SDK依赖(原生com.umeng.umsdk:common、com.umeng.umsdk:asms、com.umeng.umsdk:apm),需要注释或者删掉,否则可能会导致SDK冲突。
// build.gradle
dependencies {
/* 删除或注释原生项目有关友盟APM和Common相关的SDK,以下为示例 */
// implementation 'com.umeng.umsdk:common:9.4.4'
// implementation 'com.umeng.umsdk:asms:1.4.1'
// implementation 'com.umeng.umsdk:apm:1.5.2'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}4.2.1 预初始化并开启原生采集开关配置项
如果App不能保证在Appcalition.onCreate函数中调用UMConfigure.init初始化函数,则必须在Appcalition.onCreate函数中调用此预初始化函数。对于有延迟初始化SDK需求的开发者(不能在Application.onCreate函数中调用UMConfigure.init初始化函数),必须在Application.onCreate函数中调用UMConfigure.preInit预初始化函数(preInit耗时极少,不会影响冷启动体验),而后UMConfigure.init函数可以按需延迟调用(可以放到后台线程中延时调用,可以延迟,但还是必须调用)。如果您的App已经是在Application.onCreate函数中调用UMConfigure.init进行初始化,则无需额外调用UMConfigure.preInit预初始化函数。
public static void preInit(Context context,String appkey,String channel)<!-- 在AndroidManifest.xml中加入如下必须的权限 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>// 在.java文件中引入原生SDK包
import com.umeng.umcrash.UMCrash;
import com.umeng.commonsdk.UMConfigure;
// 在application.onCreate内配置各模块开关并预初始化SDK
Bundle bundle = new Bundle();
// 重点关注:如果您还想采集Native 崩溃、ANR等日志可以参考下面设置
bundle.putBoolean(UMCrash.KEY_ENABLE_CRASH_JAVA, true);
bundle.putBoolean(UMCrash.KEY_ENABLE_CRASH_NATIVE, true);
bundle.putBoolean(UMCrash.KEY_ENABLE_CRASH_ALL, true);
bundle.putBoolean(UMCrash.KEY_ENABLE_ANR, false);
bundle.putBoolean(UMCrash.KEY_ENABLE_PA, false);
bundle.putBoolean(UMCrash.KEY_ENABLE_LAUNCH, false);
bundle.putBoolean(UMCrash.KEY_ENABLE_MEM, false);
bundle.putBoolean(UMCrash.KEY_ENABLE_H5PAGE, false);
undle.putBoolean(UMCrash.KEY_ENABLE_POWER, false);
UMCrash.initConfig(bundle);
// 开启模块开关,上述模块开关一定要在init前调用。
UMConfigure.preInit(getApplicationContext(), "59892f08310c9307b60023d0", "UMENG", UMConfigure.DEVICE_TYPE_PHONE, "");
/************ 以下代码在纯Flutter项目中无需调用,flutter_module需要调用在预初始化后正式初始化SDK *************/
// 判断是否同意隐私协议,如果用户同意协议直接初始化umsdk,此处代码详情请参考demo https://github.com/umeng/MultiFunctionAndroidDemo
if (sharedPreferencesHelper.getSharedPreference("uminit", "").equals("1")) {
//友盟正式初始化
UmInitConfig umInitConfig = new UmInitConfig();
umInitConfig.UMinit(getApplicationContext());
}4.2.3 混淆设置
⚠️ 注意:Android下如果不设置忽略混淆,APM将无法正常运行
参考:接入与基础功能-混淆设置
步骤五、运行验证
恭喜您!至此,您的App已经成功接入了Flutter 监控啦。
接下来,可以运行您的APP,等待1到5分钟左右即可在平台上查看数据了!
因为设备采样率的关系,测试设备并一定会直接命中日志采样,所以我们需要在验证前将测试设备添加白名
单或者将Flutter PV采样率调至100%以确保测试设备可以命中日志采样
【PV采样率】解释:设备启动后产生的页面访问行为会采集异常、性能、帧率的日志,通过云配采样率控制以设备为维度的采集行为。例 PV采样率 5% 100台
设备通过端上的随机计算是否能命中那5%的概率,命中即可通过PV行为采集各类型日志。目前我们根据不同的应用权限提供了设备PV采样率和单设备日志最大条数上报的设置功能
⚠️ 提示:
免费版设备Flutter PV采样率 默认 5% 不可更改
专业版 设备Flutter PV采样率 最高 5% 尊享版设备最高可设置100% 可在 开通管理- 修改配置中 更改
支持单设备每天上报Dart异常日志的上限,免费版最高20条/天,专业版最高支持40条/天,尊享版最高支持120条/天
支持单设备每天上报性能(PV、页面性能&帧率)日志条数的上限 免费版最高支持200条/天,专业版最高支持500条/天,尊享版最高支持1000条/天
运行验证方式 | 使用权限 | 使用场景 | 采样生效时间 |
通用采样设备(设备白名单) | 免费版、专业版、尊享版 | 对专门的单个或者多个测试设备进行验证 | 设置8小时后,设备重新冷启动生效 强制生效步骤 1. SDK初始化,获取umid 2. 将此设备的umid添加到采样白名单(线上缓存最多需要5分钟生效) 3. 不要卸载安装App,修改客户端时间为8小时后 4. 重新冷启动,即可拉取到最新云配置,此设备白名单功能生效 |
调整Flutter采样率至100% | 专业版、尊享版 | 付费版应用权限下适合未上架应用对测试设备进行验证,采样率调整会对设置比例下的访问设备生效。 | 设置8小时后,设备重新冷启动生效 强制生效步骤 1. SDK初始化,获取umid 2. 将此设备的umid添加到采样白名单(线上缓存最多需要5分钟生效) 3. 不要卸载安装App,修改客户端时间为8小时后 4. 重新冷启动,即可拉取到最新云配置,此设备白名单功能生效 |
5.1 验证前准备方式一
5.1.1 打开设备通用采样设置
添加白名单设备不受采样率限制,可直接用来测试日志上报情况

5.1.2 添加采样设备白名单
获取UMID 方式一 通过日志查看 (支持版本 v2.1.3)
获取UMID 方式二通过脚本获取打印UMID
Android :UMConfigure.getUMIDString(this);
iOS:[UMConfigure umidString]
将获得的UMID 填写到应用后台 设备管理 -> 通用采样设置

⚠️ 注意:
设置完成后设备白名单状态更新8小时以内客户端可生效,请提前添加测试设备
umid
希望强制生效参考如下
运行验证方式 | 使用权限 | 使用场景 | 采样生效时间 |
通用采样设备(设备白名单) | 免费版、专业版、尊享版 | 对专门的单个或者多个测试设备进行验证 | 设置8小时后,设备重新冷启动生效 强制生效步骤 1. SDK初始化,获取umid 2. 将此设备的umid添加到采样白名单(线上缓存最多需要5分钟生效) 3. 不要卸载安装App,修改客户端时间为8小时后 4. 重新冷启动,即可拉取到最新云配置,此设备白名单功能生效 |
5.2 验证前准备方式二
5.2.1 调整采样率
调整Flutter分析PV采样率至100% (免费版无法操作)

运行验证方式 | 使用权限 | 使用场景 | 采样生效时间 |
调整Flutter采样率至100% | 专业版、尊享版 | 付费版应用权限下适合未上架应用对测试设备进行验证,采样率调整会对设置比例下的访问设备生效。 | 设置8小时后,设备重新冷启动生效 强制生效
|
5.2 验证SDK运行
查看日志面板



