Flutter App Integration¶
Prerequisites¶
Note: If you have subscribed to the RUM Headless service, the prerequisites have been automatically configured for you. You can proceed directly with app integration.
- Install DataKit;
- Configure the RUM Collector;
- Configure DataKit for public network access and install the IP geolocation database.
App Integration¶
Note
The current Flutter version only supports Android and iOS platforms.
- Navigate to RUM > Create Application > Android/iOS;
- Create two separate applications for Flutter Android and Flutter iOS to receive RUM data from the Android and iOS platforms respectively;
- Enter the corresponding application name and application ID for each platform's application;
- Choose the application integration method:
- Public DataWay: Directly receives RUM data without installing the DataKit collector.
- Local Environment Deployment: Receives RUM data after meeting the prerequisites.
Installation¶
Pub.Dev: ft_mobile_agent_flutter
Source Code: https://github.com/GuanceCloud/datakit-flutter
Demo: https://github.com/GuanceCloud/datakit-flutter/example
Run the Flutter command in your project directory:
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):
dependencies:
ft_mobile_agent_flutter: [lastest_version]
# For Flutter 2.0 compatibility, use the following reference method
ft_mobile_agent_flutter:
git:
url: https://github.com/GuanceCloud/datakit-flutter.git
ref: [github_legacy_lastest_tag]
Now in your Dart code, you can use:
Additional Android Configuration
- Configure the Gradle Plugin ft-plugin to collect App launch events and native Android events (page navigation, click events, Native network requests, WebView data).
- Create a custom
Applicationand declare its use inAndroidMainifest.xml, as shown in the code below.
import io.flutter.app.FlutterApplication
/**
* A custom Application is required here to track [Launch Count] and [Launch Time]
*/
class CustomApplication : FlutterApplication() {
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cloudcare.ft.mobile.sdk.agent_example">
<application android:name=".CustomApplication">
//...
</application>
</manifest>
SDK Initialization¶
Basic Configuration¶
void main() async {
WidgetsFlutterBinding.ensureInitialized();
//Local Environment Deployment, Datakit Deployment
await FTMobileFlutter.sdkConfig(
datakitUrl: datakitUrl
);
//Use Public DataWay
await FTMobileFlutter.sdkConfig(
datawayUrl: datawayUrl,
cliToken: cliToken,
);
}
| Field | Type | Required | Description |
|---|---|---|---|
| datakitUrl | String | Yes | The reporting URL address for Local Environment Deployment (Datakit), e.g., http://10.0.0.1:9529. The default port is 9529. The device installing the SDK must be able to access this address. Note: Configure either datakitUrl OR datawayUrl. |
| datawayUrl | String | Yes | The reporting URL address for Public DataWay, obtained from the [RUM] application, e.g., https://open.dataway.url. The device installing the SDK must be able to access this address. Note: Configure either datakitUrl OR datawayUrl. |
| cliToken | String | Yes | Authentication token, must be configured together with datawayUrl. |
| debug | bool | No | Sets whether to allow log printing, default is false. |
| env | String | No | Environment configuration, default is prod. Any string is allowed, a single word like test is recommended. |
| envType | enum EnvType | No | Environment configuration, default is EnvType.prod. Note: Configure either env OR envType. |
| autoSync | bool | No | Whether to automatically sync data to the server after collection, default is true. When false, use FTMobileFlutter.flushSyncData() to manage data synchronization manually. |
| syncPageSize | enum | No | Sets the number of entries per sync request: SyncPageSize.mini (5 entries), SyncPageSize.medium (10 entries), SyncPageSize.large (50 entries). Default is SyncPageSize.medium. |
| customSyncPageSize | number | No | Sets the number of entries per sync request. Range [5, ∞). Note: A larger number means the sync uses more computational resources. |
| syncSleepTime | number | No | Sets the sync interval time. Range [0,5000] milliseconds. Default is not set. |
| globalContext | object | No | Adds custom tags. For adding rules, please refer to here. |
| serviceName | String | No | Service name. |
| enableLimitWithDbSize | boolean | No | Enables limiting data size using the database, default limit is 100MB (unit: Byte). A larger database increases disk pressure. Default is disabled. Note: Once enabled, the Log configuration logCacheLimitCount and RUM configuration rumCacheLimitCount become invalid. Supported in SDK version 0.5.3-pre.2 and above. |
| dbCacheLimit | number | No | DB cache limit size. Range [30MB, ∞), default is 100MB, unit: byte. Supported in SDK version 0.5.3-pre.2 and above. |
| dbCacheDiscard | string | No | Sets the data discard rule for the database. Discard strategies: FTDBCacheDiscard.discard discards new data (default), FTDBCacheDiscard.discardOldest discards oldest data. Supported in SDK version 0.5.3-pre.2 and above. |
| compressIntakeRequests | boolean | No | Compresses upload sync data using deflate. Supported in SDK version 0.5.3-pre.2 and above, default is disabled. |
| enableDataIntegerCompatible | boolean | No | Recommended to enable when coexistence with web data is needed. This configuration handles web data type storage compatibility issues. Enabled by default in SDK version 0.5.4-pre.1 and above. |
| dataModifier | Map |
No | Modifies individual fields. |
| lineDataModifier | Map |
No | Modifies individual data lines. |
RUM Configuration¶
| Field | Type | Required | Description |
|---|---|---|---|
| androidAppId | String | Yes | app_id, applied for in the RUM console. |
| iOSAppId | String | Yes | app_id, applied for in the RUM console. |
| sampleRate | double | No | Sampling rate, range [0,1]. 0 means no collection, 1 means full collection. Default is 1. Applies to all View, Action, LongTask, Error data under the same session_id. |
| sessionOnErrorSampleRate | double | No | Sets the error collection rate. When a session is not sampled by setSamplingRate, if an error occurs during the session, data from 1 minute before the error can be collected. Range [0,1]. 0 means no collection, 1 means full collection. Default is 0. Applies to all View, Action, LongTask, Error data under the same session_id. |
| enableUserResource | bool | No | Whether to enable automatic capture of http Resource data, default is false. This is implemented by modifying HttpOverrides.global. If the project has custom requirements, inherit FTHttpOverrides. |
| enableNativeUserAction | bool | No | Whether to track Native Action, including native system Button click events and app launch events, default is false. |
| enableNativeUserView | bool | No | Whether to enable automatic Native View tracking. Recommended to disable for pure Flutter apps, default is false. |
| enableNativeUserViewInFragment | bool | No | Whether to automatically track Native Fragment type page data, default is false. Android only. |
| enableNativeUserResource | bool | No | Whether to enable automatic Native Resource tracking. Recommended to disable for pure Flutter apps, default is false. |
| enableAppUIBlock | bool | No | Whether to enable automatic Native Freeze tracking, default is false. |
| nativeUiBlockDurationMS | int | No | Sets the time threshold for Native Freeze, range [100, ∞) milliseconds. iOS default 250ms, Android default 1000ms. |
| enableTrackNativeAppANR | bool | No | Whether to enable Native ANR monitoring, default is false. |
| enableTrackNativeCrash | bool | No | Whether to enable monitoring for Android Java Crash and OC/C/C++ crashes, default is false. |
| errorMonitorType | enum ErrorMonitorType | No | Sets auxiliary monitoring information, adding additional monitoring data to RUM Error data. ErrorMonitorType.battery for battery level, ErrorMonitorType.memory for memory usage, ErrorMonitorType.cpu for CPU usage. Default is disabled. |
| deviceMetricsMonitorType | enum DeviceMetricsMonitorType | No | Adds monitoring data during the View lifecycle. DeviceMetricsMonitorType.battery (Android only) monitors the highest current output on the current page, DeviceMetricsMonitorType.memory monitors the app's memory usage, DeviceMetricsMonitorType.cpu monitors CPU ticks, DeviceMetricsMonitorType.fps monitors screen frame rate. Default is disabled. |
| detectFrequency | enum DetectFrequency | No | View performance monitoring sampling frequency, default is DetectFrequency.normal. |
| globalContext | Map | No | Custom global parameters. |
| rumCacheDiscard | enum | No | Discard strategy: FTRUMCacheDiscard.discard discards new data (default), FTRUMCacheDiscard.discardOldest discards oldest data. |
| rumCacheLimitCount | number | No | Local cache maximum RUM entry limit [10_000, ∞), default is 100_000. |
| isInTakeUrl | callBack | No | Sets conditions for filtering Resources, no filtering by default. |
Adding Custom Tags¶
Static Usage¶
- Split the original main.dart into 2 parts: one part is main(), the other part is the App() MaterialApp widget;
- Create entry files for various environments, e.g., main_prod.dart, main_gray.dart, etc.;
- Configure custom tags in the corresponding environment file. For example:
///main_prod.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
//Initialize SDK
await FTMobileFlutter.sdkConfig(
datakitUrl: serverUrl,
debug: true,
);
await FTRUMManager().setConfig(
androidAppId: appAndroidId,
iOSAppId: appIOSId,
globalContext: {CUSTOM_STATIC_TAG:"prod_static_tag"},
);
runApp(MyApp());
};
Dynamic Usage¶
- Use a file-based data storage, e.g., the
SharedPreferenceslibrary'sSharedPreferences, to configure theSDK. Add code to fetch tag data at the configuration point.
final prefs = await SharedPreferences.getInstance();
String customDynamicValue = prefs.getString("customDynamicValue")?? "not set";
await FTRUMManager().setConfig(
androidAppId: appAndroidId,
iOSAppId: appIOSId,
globalContext: {CUSTOM_DYNAMIC_TAG:customDynamicValue},
//… Add other configurations
);
- Add a method to change the file data anywhere.
static Future<void> setDynamicParams(String value) async{
final prefs = await SharedPreferences.getInstance();
prefs.setString(CUSTOM_DYNAMIC_TAG, value);
}
- Finally, restart the application.
Note:
- Special key: track_id (used for tracing functionality).
- When a user adds a custom tag via
globalContextthat conflicts with an SDK built-in tag, the SDK's tag will override the user's. It is recommended to prefix tag names with a project abbreviation, e.g.,df_tag_name. Thekeyvalues used in the project can be queried from the source code.
Log Configuration¶
| Field | Type | Required | Description |
|---|---|---|---|
| sampleRate | double | No | Sampling rate, range [0,1]. 0 means no collection, 1 means full collection. Default is 1. |
| enableLinkRumData | bool | No | Whether to associate with RUM data. |
| enableCustomLog | bool | No | Whether to enable custom logs. |
| logLevelFilters | List |
No | Log level filtering. |
| logCacheLimitCount | int | No | Local cache maximum log entry limit [1000, ∞). A larger log means greater disk cache pressure. Default is 5000. |
| discardStrategy | enum FTLogCacheDiscard | No | Sets the log discard rule when the limit is reached. Default is FTLogCacheDiscard.discard. discard discards new data, discardOldest discards oldest data. |
Trace Configuration¶
| Field | Type | Required | Description |
|---|---|---|---|
| sampleRate | double | No | Sampling rate, range [0,1]. 0 means no collection, 1 means full collection. Default is 1. |
| traceType | enum TraceType | No | Trace type, default is TraceType.ddTrace. Options: TraceType.ddTrace, TraceType.zipkinMulti, TraceType.zipkinSingle, TraceType.traceparent, TraceType.skywalking, TraceType.jaeger. |
| enableLinkRUMData | bool | No | Whether to associate with RUM data, default is false. |
| enableAutoTrace | bool | No | Whether to add Trace Header to http requests, default is false. This is implemented by modifying HttpOverrides.global. If the project has custom requirements, inherit FTHttpOverrides. |
| enableNativeAutoTrace | bool | No | Whether to enable native network auto-tracing for iOS NSURLSession and Android OKhttp, default is false. |
RUM User Data Tracking¶
Action¶
Usage¶
/// Add an action
/// [actionName] action name
/// [actionType] action type
/// [property] additional property parameters (optional)
Future<void> startAction(String actionName, String actionType,
{Map<String, String>? property})
Code Example¶
View¶
Automatic Collection¶
- Method 1: Add
FTRouteObservertoMaterialApp.navigatorObservers, set the pages to navigate to inMaterialApp.routes. Thekeyinroutesbecomes the page name (view_name).
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeRoute(),
navigatorObservers: [
//RUM View: Monitor page lifecycle when using route navigation
FTRouteObserver(),
],
routes: <String, WidgetBuilder>{
//set Route route navigation
'logging': (BuildContext context) => Logging(),
'rum': (BuildContext context) => RUM(),
'tracing_custom': (BuildContext context) => CustomTracing(),
'tracing_auto': (BuildContext context) => AutoTracing(),
},
);
}
}
//Page navigation via this method, the page name here is 'logging'
Navigator.pushNamed(context, "logging");
- Method 2: Add
FTRouteObservertoMaterialApp.navigatorObservers, use a customFTMaterialPageRouteto derive the page name from the widget's runtimeType. Thewidgetclass name becomes the page name (view_name). See an example here.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeRoute(),
navigatorObservers: [
//RUM View: Monitor page lifecycle when using route navigation
FTRouteObserver(),
],
);
}
}
//The "page name" here is NoRouteNamePage
Navigator.of(context).push(FTMaterialPageRoute(builder: (context) =>
new NoRouteNamePage()
- Method 3: Add
FTRouteObservertoMaterialApp.navigatorObservers, customize theRouteSettings.nameproperty inRoutetype pages. The collection logic ofFTRouteObserverwill prioritize the value assigned toRouteSettings.name. This method also applies to Dialog type pages, e.g.,showDialog(),showTimePicker(), etc.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeRoute(),
navigatorObservers: [
//RUM View: Monitor page lifecycle when using route navigation
FTRouteObserver(),
],
);
}
}
//The "page name" here is "RouteSettingName"
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => new NoRouteNamePage(),
settings: RouteSettings(name: "RouteSettingName"))
-
The above three methods can be mixed within a single project.
-
Sleep and wake event collection For versions below 0.5.1-pre.1, to collect application sleep and wake behaviors, add the following code:
class _HomeState extends State<HomeRoute> {
@override
void initState(){
//Add application sleep and wake listeners
FTLifeRecycleHandler().initObserver();
}
@override
void dispose(){
//Remove application sleep and wake listeners
FTLifeRecycleHandler().removeObserver();
}
}
Automatic Collection Filtering¶
Supported only in version 0.5.0-pre.1 and above.
FTRouteObserver
MaterialApp(
navigatorObservers: [
// RUM View: routeFilter filters out pages that should not be monitored
FTRouteObserver(routeFilter: (Route? route, Route? previousRoute) {
if (filterConfig) {
//Do not collect
return true;
}
return false;
}),
])
| Field | Type | Required | Description |
|---|---|---|---|
| routeFilter | RouteFilter | No | Page method callback. Can judge based on the incoming and previous route. Returns true to filter data meeting the condition, false otherwise. |
FTDialogRouteFilterObserver
Filters DialogRoute type pages, e.g., showDialog(), showTimePicker(), etc.
MaterialApp(
navigatorObservers: [
//RUM View filters DialogRoute type components
FTDialogRouteFilterObserver(filterOnlyNoSettingName: true)
])
// This Dialog will be collected if filterOnlyNoSettingName is true.
// view_name will be "About"
showAboutDialog(
context: context, routeSettings: RouteSettings(name: "About"));
| Field | Type | Required | Description |
|---|---|---|---|
| filterOnlyNoSettingName | bool | No | Only filters Route pages where RouteSettings.name is null. |
Custom View¶
Usage¶
/// Create a view. This method must be called before [starView]. Currently not available in flutter route.
/// [viewName] interface name
/// [duration]
Future<void> createView(String viewName, int duration)
/// Start a view
/// [viewName] interface name
/// [viewReferer] previous interface name
/// [property] additional property parameters (optional)
Future<void> starView(String viewName, {Map<String, String>? property})
/// Stop a view
/// [property] additional property parameters (optional)
Future<void> stopView({Map<String, String>? property})
Code Example¶
FTRUMManager().createView("Current Page Name", 100000000)
FTRUMManager().starView("Current Page Name");
FTRUMManager().stopView();
Error¶
Automatic Collection¶
void main() async {
runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
await FTMobileFlutter.sdkConfig(
datakitUrl: serverUrl,
debug: true,
);
await FTRUMManager().setConfig(
androidAppId: appAndroidId,
iOSAppId: appIOSId,
);
// Flutter exception capture
FlutterError.onError = FTRUMManager().addFlutterError;
runApp(MyApp());
}, (Object error, StackTrace stack) {
//Add Error data
FTRUMManager().addError(error, stack);
});
Custom Error¶
Usage¶
///Add a custom error
/// [stack] stack log
/// [message] error message
/// [appState] application state
/// [errorType] custom errorType
/// [property] additional property parameters (optional)
Future<void> addCustomError(String stack, String message,
{Map<String, String>? property, String? errorType})
Code Example¶
Resource¶
Automatic Collection¶
Enable enableUserResource in the configuration FTRUMManager().setConfig.
Custom Resource¶
Usage¶
///Start a resource request
/// [key] unique id
/// [property] additional property parameters (optional)
Future<void> startResource(String key, {Map<String, String>? property})
///End a resource request
/// [key] unique id
/// [property] additional property parameters (optional)
Future<void> stopResource(String key, {Map<String, String>? property})
/// Send resource data metrics
/// [key] unique id
/// [url] request URL
/// [httpMethod] request method
/// [requestHeader] request header parameters
/// [responseHeader] response header parameters
/// [responseBody] response content
/// [resourceStatus] response status code
Future<void> addResource(
{required String key,
required String url,
required String httpMethod,
required Map<String, dynamic> requestHeader,
Map<String, dynamic>? responseHeader,
String? responseBody = "",
int? resourceStatus})
Code Example¶
/// Using httpClient
void httpClientGetHttp(String url) async {
var httpClient = new HttpClient();
String key = Uuid().v4();
HttpClientResponse? response;
HttpClientRequest? request;
try {
request = await httpClient
.getUrl(Uri.parse(url))
.timeout(Duration(seconds: 10));
FTRUMManager().startResource(key);
response = await request.close();
} finally {
Map<String, dynamic> requestHeader = {};
Map<String, dynamic> responseHeader = {};
request!.headers.forEach((name, values) {
requestHeader[name] = values;
});
var responseBody = "";
if (response != null) {
response.headers.forEach((name, values) {
responseHeader[name] = values;
});
responseBody = await response.transform(Utf8Decoder()).join();
}
FTRUMManager().stopResource(key);
FTRUMManager().addResource(
key: key,
url: request.uri.toString(),
requestHeader: requestHeader,
httpMethod: request.method,
responseHeader: responseHeader,
resourceStatus: response?.statusCode,
responseBody: responseBody,
);
}
}
For usage with the http library and dio library, refer to the example.
Logger Logging¶
Custom Logs¶
The log content is currently limited to 30 KB. Characters beyond this limit will be truncated.
Usage¶
///Output a log
///[content] log content
///[status] log status
///[property] additional property parameters (optional)
Future<void> logging(String content, FTLogStatus status, {Map<String, String>? property})
Code Example¶
Log Levels¶
| Method Name | Meaning |
|---|---|
| FTLogStatus.info | Info |
| FTLogStatus.warning | Warning |
| FTLogStatus.error | Error |
| FTLogStatus.critical | Critical |
| FTLogStatus.ok | Ok |
Tracer Network Tracing¶
Automatic Collection¶
Enable enableAutoTrace in the configuration FTTracer().setConfig.
Custom Tracer¶
Usage¶
/// Get trace http request header data
/// [key] unique id
/// [url] request URL
///
Future<Map<String, String>> getTraceHeader(String url, {String? key})
Code Example¶
/// Using httpClient
void httpClientGetHttp() async {
var url = 'http://reqeust.url.cn';
var httpClient = new HttpClient();
String key = DateTime.now().millisecondsSinceEpoch.toString() + url;
var errorMessage = "";
HttpClientRequest request = await httpClient.getUrl(Uri.parse(url));
HttpClientResponse? response;
try {
final traceHeaders =
await FTTracer().getTraceHeader(key, request.uri.toString());
traceHeaders.forEach((key, value) {
request.headers.add(key, value);
});
response = await request.close();
} catch (exception) {
errorMessage = exception.toString();
} finally {
Map<String, dynamic> requestHeader = {};
Map<String, dynamic> responseHeader = {};
request.headers.forEach((name, values) {
requestHeader[name] = values;
});
if (response != null) {
response.headers.forEach((name, values) {
responseHeader[name] = values;
});
}
}
}
For usage with the http library and dio library, refer to the example.
User Information Binding and Unbinding¶
FTMobileFlutter¶
Usage¶
///Bind a user
///
///[userid] user id
///[userName] user name
///[userEmail] user email
///[userExt] extension data
static Future<void> bindRUMUserData(String userId,
{String? userName, String? userEmail, Map<String, String>? ext})
///Unbind a user
static Future<void> unbindRUMUserData()
Code Example¶
Manual Data Synchronization¶
FTMobileFlutter¶
WebView Data Monitoring¶
WebView data monitoring requires integrating the Web Monitoring SDK into the WebView access page.
Data Masking¶
If you want to fully mask a field, it is recommended to use dataModifier for better performance. For detailed rule replacement, use lineDataModifier.
await FTMobileFlutter.sdkConfig(
dataModifier: {"device_uuid":"xxx"},
lineDataModifier: {"view":{"view_name":"xxx"}}
)
Native and Flutter Hybrid Development¶
If your project is natively developed with some pages or business processes implemented using Flutter, the SDK installation and initialization configuration method is as follows:
- Installation: The installation method remains the same.
- Initialization: Please refer to iOS SDK Initialization Configuration and Android SDK Initialization Configuration for initialization configuration within the native project.
- Flutter Configuration:
- View, Resource, Error use the same configuration method as pure Flutter projects.
- Flutter Resource and Trace automatic collection use the following configuration method: