Flutter SDK
Integration guide for the Quick Tracking APM SDK Flutter plugin.
Stable app performance is critical to a good user experience. The lightweight Quick Tracking APM SDK Flutter plugin provides real-time, reliable, and comprehensive monitoring of page performance, page frame rate, Dart exceptions, and custom exceptions. It helps developers efficiently reproduce issues and understand the user paths and business context behind exceptions and jank.
1. Prerequisites
Android: Requires APM SDK v2.0.0 or later. Add the component library dependency to the
dependenciessection of your app module'sbuild.gradlefile:api 'com.lydaas.qtsdk:apm-efs:2.0.4'
iOS: Requires APM SDK v2.0.0 or later. Add the following to the
Podfilein your project's root directory:pod 'UMEFS_P', '2.2.0'
Flutter Common Version: Add the following dependency to your project's
pubspec.yamlfile:qt_common_sdk: ^2.1.2
Flutter APM version: Add the following dependency to your project's
pubspec.yamlfile:qt_apm_sdk: ^2.6.1
Version dependencies
qt_common_sdk: ^2.1.2:
Depends on native iOS version 1.7.1.PX.
- Depends on native Android version 1.8.0.PX.
qt_apm_sdk: ^2.6.0:
|---Depends on native iOS
|-------UMAPM_P: 2.2.0
| -------UMEFS_P: 2.2.0
|---Dependency on native Android
|-------apm-crash:2.0.0
| -------apm-efs:2.0.4
Appkey
During SDK initialization, you must provide the Appkey parameter. The Appkey is a unique identifier for your application in Quick Tracking, generated when you create the app. To obtain or view it, see Application Management.
Data collection domain
Obtain it from the Data Collection module in the management console.
2. Integrate the Flutter APM plugin
The APM SDK depends on the statistical analysis SDK. You can integrate either the Flutter version or the native versions of the statistical analysis SDK. The Flutter APM and Flutter Common SDKs already include the Android and iOS SDKs. If your project is a pure Flutter app, you only need to integrate the Flutter SDKs.
For instructions on how to integrate the Flutter statistical analysis SDK, see qt_common_sdk.
For instructions on how to integrate the iOS statistical analysis SDK, see iOS SDK.
For instructions on how to integrate the Android statistical analysis SDK, see Android SDK.
Note:
Since the Flutter SDK includes native APM and Common SDK dependencies, do not add them again to your project. If your native project's CocoaPods or manual dependencies already include other Quick Tracking SDKs (such as native QTCommon or native APM), remove them to avoid conflicts.
Integration steps
Add the following dependencies to your project's pubspec.yaml file:
# Production dependencies
dependencies:
qt_common_sdk: ^2.1.2 // Statistical analysis
qt_apm_sdk: ^2.6.1 // Performance monitoringNote: If you need compatibility with Flutter v2.8.1, you can integrate qt_apm_sdk: ^2.3.0-flutter-2.8.1.
Import the package:
import 'package:qt_apm_sdk/qt_apm_sdk.dart';Obfuscation configuration
By default, flutter build apk enables R8. If your application uses code obfuscation, add the following rules to prevent the Quick Tracking SDK from being incorrectly obfuscated, which can cause it to malfunction.
# Keep Quick Tracking SDK classes (replace the package name)
-keep class com.quick.qt.** { *; }
-keep class com.umeng.** { *; }
-keep class com.uc.** { *; }
-keep class com.efs.** { *; }
-dontwarn com.quick.qt.analytics.middle.DevLog
# Keep all Okio-related classes (OkHttp dependency)
-dontwarn okio.**
-dontwarn javax.annotation.**
-dontwarn okhttp3.**
-keepclassmembers class *{
public<init>(org.json.JSONObject);
}
-keepclassmembers enum *{
publicstatic**[] values();
publicstatic** valueOf(java.lang.String);
}The SDK uses reflection to access resource files (R.java) imported into your project. Obfuscation or optimization tools like Proguard may remove this file. If this occurs, add the following configuration rule and replace [your app's package name] with your app's package name:
-keep public class [your app's package name].R$*{
public static final int *;
} 2.1 Initialization settings
final QuickTrackingFlutterApmSdk qtApmSdk = QuickTrackingFlutterApmSdk(
name: '',
bver: '',
flutterVersion: 'your Flutter version',
engineVersion: 'your Flutter engine version',
enableLog: true,
enableTrackingPageFps: true,
enableTrackingPagePerf: true,
errorFilter: {
"mode": "ignore",
"rules": [],
},
trackDomain: "your data collection domain",
initFlutterBinding: MyApmWidgetsFlutterBinding.ensureInitialized,
// onError: (exception, stack) {},
);Parameters:
Parameter | Description | Required | Type |
name | Application or module name. | Yes | string |
bver | Application or module version and build number. | Yes | string |
flutterVersion | Flutter SDK version (default: empty string). | No | string |
engineVersion | Flutter engine version. | No | string |
enableLog | Enables SDK logging (default: | No | boolean |
enableTrackingPageFps | Enables page frame rate monitoring (default: | No | boolean |
enableTrackingPagePerf | Enables page performance monitoring (default: | No | boolean |
errorFilter | Configures a blacklist and whitelist for captured exceptions. | No | map |
trackDomain | The data collection domain. | Yes | string |
initFlutterBinding | The override and initialization method for | No | function |
onError | A callback that executes when an exception is thrown. | No | function |
2.2 SDK initialization
Initializing the SDK enables its monitoring features.
Remove the original
WidgetsFlutterBinding.ensureInitialized()call to prevent duplicate binding initialization errors.The SDK handles binding initialization internally via the
initFlutterBindingparameter.Call any code that depends on
ensureInitialized()within this callback.You can use this callback to asynchronously fetch and set the application name and version number.
You can instantiate the SDK with empty strings for
nameandbver, and then assign their values within theinitcallback.
Complete initialization example
import 'package:qt_apm_sdk/qt_apm_sdk.dart';
void main() {
final QuickTrackingFlutterApmSdk qtApmSdk = QuickTrackingFlutterApmSdk(
name: '',
bver: '',
flutterVersion: '3.10.0',
engineVersion: 'd44b5a94c9',
enableLog: true,
enableTrackingPageFps: true,
enableTrackingPagePerf: true,
errorFilter: {
"mode": "ignore",
// "rules": [RegExp('RangeError')],
"rules": [],
},
trackDomain: "your_data_collection_domain",
initFlutterBinding: MyApmWidgetsFlutterBinding.ensureInitialized,
// onError: (exception, stack) {},
);
qtApmSdk.init(appRunner: (observer) async {
// Ensure you remove the original `WidgetsFlutterBinding.ensureInitialized()` call
// to prevent duplicate binding initialization errors.
// The SDK handles binding initialization internally.
// Code that depends on `ensureInitialized()` can be called here.
// Asynchronously set the app name and version here.
// For example, if you instantiate the SDK with empty strings for name and bver:
qtApmSdk.name = 'app_demo';
qtApmSdk.bver = '1.0.0+9';
return MyApp(observer);
});
}
class MyApmWidgetsFlutterBinding extends ApmWidgetsFlutterBinding {
@override
void handleAppLifecycleStateChanged(AppLifecycleState state) {
// Add your custom implementation logic.
// print('AppLifecycleState changed to $state');
super.handleAppLifecycleStateChanged(state);
}
static WidgetsBinding ensureInitialized() {
// Bind only after initialization is complete.
if (WidgetsBinding.instance == null) {
MyApmWidgetsFlutterBinding();
}
return WidgetsBinding.instance;
}
}2.3 Register the observer
Register NavigatorObserver in MyApp (StatelessWidget or StatefulWidget) by implementing the MyApp(this._navigatorObserver) constructor and adding ApmNavigatorObserver.singleInstance to navigatorObservers.
class MyApp extends StatelessWidget {
MyApp([this._navigatorObserver]);
final NavigatorObserver? _navigatorObserver;
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
routes: routes,
initialRoute: "/",
navigatorObservers: <NavigatorObserver>[
_navigatorObserver ?? ApmNavigatorObserver.singleInstance
],
);
}
}3. SDK API
3.1 Page monitoring
Page performance monitoring
Page performance monitoring is disabled by default. To enable it, set enableTrackingPagePerf to true.
Example:
final QuickTrackingFlutterApmSdk qtApmSdk = QuickTrackingFlutterApmSdk(
...
enableTrackingPagePerf: true,
...
);
qtApmSdk.init(appRunner: (observer) async {
qtApmSdk.name = 'qt_demo';
qtApmSdk.bver = 'app_version+build_number';
return MyApp(observer);
});Page frame rate analysis
Page frame rate (FPS) indicates the number of frames rendered per second, a key metric for measuring smoothness and dynamic content performance. To use this feature, you must use an ApmScrollController instance and register it to monitor scrolling events.
import 'package:flutter/material.dart';
import 'package:qt_apm_sdk/qt_apm_sdk.dart';
class ScrollLazyLoadPage extends StatefulWidget {
@override
_ScrollLazyLoadPageState createState() => _ScrollLazyLoadPageState();
}
class _ScrollLazyLoadPageState extends State<ScrollLazyLoadPage> {
List<String> imageUrls = [];
int page = 1;
// Use the APM scroll controller (ApmScrollController).
final 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(const Duration(seconds: 2));
final List<String> urls = [
'https://img_01.jpg',
'https://img_02.jpg',
'https://img_03.jpg',
...
];
try {
setState(() {
imageUrls.addAll(urls);
isLoading = false;
});
} catch (e) {}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Scroll Lazy Load Demo'),
),
body: GridView.builder(
controller: _scrollController,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemCount: imageUrls.length + 1,
itemBuilder: (context, index) {
if (index == imageUrls.length) {
return Center(
child:
isLoading ? const CircularProgressIndicator() : const SizedBox.shrink(),
);
}
return Card(
child: Image.network(
imageUrls[index],
fit: BoxFit.cover,
),
);
},
),
);
}
}3.2 Exception monitoring
Dart exceptions
The SDK automatically monitors Dart exceptions, including synchronous and asynchronous exceptions (managed by runZonedGuarded), framework exceptions, and custom-reported exceptions. You can view the reported data on the platform.
Note: You can set a daily limit on the number of Dart exceptions reported per device. The default is 20, and the maximum is 120 per day.
Custom exceptions
captureException (Type: Function)
Parameter | Description | Required | Type |
exception | Exception summary. | Yes | Exception |
stack | The exception's stack trace. | No | String |
extra | Custom attributes. | No | Map<String, dynamic> |
Example 1
import 'package:qt_apm_sdk/qt_apm_sdk.dart';
void main() async {
Isolate isolate = await Isolate.spawn(runIsolate, []);
// Listen for isolate exceptions.
isolate.addErrorListener(RawReceivePort((pair) {
var error = pair[0];
var stacktrace = pair[1];
// Manually capture the isolate exception.
ExceptionTrace.captureException(
exception: Exception(error),
stack: stacktrace.toString());
}).sendPort);
}Example 2
import 'package:qt_apm_sdk/qt_apm_sdk.dart';
void main() {
try {
List<String> numList = ['1', '2'];
print(numList[5]);
} catch (e) {
// Manually capture and report a code execution exception.
ExceptionTrace.captureException(
exception: Exception(e), extra: {"user": '123'});
}
}3.3 ErrorFilter configuration
Use errorFilter to configure a blacklist or whitelist for captured exceptions. A whitelist reports only exceptions matching the rules, while a blacklist ignores those that match.
This is an optional parameter for filtering logs and includes the following properties:
Property | Description | Default | Type |
mode | The matching mode.
| ignore | Enum: |
rules | A collection of matching rules.
|
| Array<string | RegExp> |
Example:
void main() {
QuickTrackingFlutterApmSdk(
name: 'your_app_or_module_name',
// Filter exceptions.
errorFilter: {
"mode": "match",
"rules": [RegExp('RangeError')],
},
...
);
}4. Native SDK integration
For iOS SDK integration, see iOS SDK.
For Android SDK integration, see Android SDK.
5. Remote configuration
Remote configuration allows you to set the device PV sampling rate and the maximum number of logs reported per device. Changes to the sampling rate take about 15 minutes to propagate from the server. Once active on the server, the new configuration is fetched by the SDK on the next app cold start and applied on the one after.
PV sampling rate: This setting controls which devices report data. When an app starts, an on-device calculation determines if the device meets the sampling criteria (e.g., a 5% rate). If the device is sampled, the SDK collects logs for exceptions, performance, and frame rates for that session.
Default rules:
Flutter monitoring: Enabled by default.
PV sampling rate: 5% by default, adjustable up to 100%. This rate also affects Flutter monitoring; if a device is not sampled, Flutter monitoring is also disabled for that session.
Page performance limit: The maximum number of page performance logs reported per device per day. Default: 200. Maximum: 1,000.
Dart exception limit: The maximum number of Dart exceptions reported per device per day. Default: 20. Maximum: 120.
6. SDK verification
Adjust sampling rate
Before verification, set the Flutter PV sampling rate to 100% to ensure all log samples are captured. You can adjust this setting for your production environment as needed.

View log panel

If you cannot find any data, go to Switch and sampling configuration and ensure that Flutter Monitoring is Enabled (it is enabled by default).
