Flutter Application Integration¶
Prerequisites¶
Note: If you have activated the RUM Headless service, the prerequisites have been automatically configured. You can directly integrate the application.
- Install DataKit;
- Configure the RUM Collector;
- Configure DataKit to be publicly accessible and install the IP geolocation database: Installation Guide.
Application Integration¶
The current Flutter version temporarily supports only Android and iOS platforms. Log in to the Guance console and navigate to the User Access Monitoring page. Click Create Application in the upper-left corner to begin creating a new application.
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 following Flutter command in your project directory:
This will add a line like the following to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
ft_mobile_agent_flutter: [latest_version]
# Use the following reference method for compatibility with Flutter 2.0
ft_mobile_agent_flutter:
git:
url: https://github.com/GuanceCloud/datakit-flutter.git
ref: [github_legacy_latest_tag]
Now, in your Dart code, you can use:
Android Integration Additional Configuration
- Configure Gradle Plugin ft-plugin to collect App startup events and Android Native-related events (page transitions, click events, Native network requests, WebView data).
- Customize the
Application
, declare it inAndroidMainifest.xml
, and use the following code.
import io.flutter.app.FlutterApplication
/**
* Custom Application is required if you need to count the number of app startups and measure startup 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
);
// Using public Dataway
await FTMobileFlutter.sdkConfig(
datawayUrl: datawayUrl,
cliToken: cliToken,
);
}
Field | Type | Required | Description |
---|---|---|---|
datakitUrl | String | Yes | URL address for reporting in local environment deployment (Datakit), example: http://10.0.0.1:9529, default port is 9529. Devices installing the SDK must be able to access this address. Note: Either datakitUrl or datawayUrl must be configured |
datawayUrl | String | Yes | Public Dataway reporting URL address, obtained from the [User Access Monitoring] application, example: https://open.dataway.url, devices installing the SDK must be able to access this address. Note: Either datakitUrl or datawayUrl must be configured |
cliToken | String | Yes | Authentication token, must be used together with datawayUrl |
debug | bool | No | Whether to allow logging, default false |
env | String | No | Environment configuration, default prod , any characters are acceptable, but it is recommended to use a single word, such as test etc. |
envType | enum EnvType | No | Environment configuration, default EnvType.prod . Note: Only one of env and envType needs to be configured |
autoSync | bool | No | Whether to automatically synchronize collected data to the server, default true . When set to false , use FTMobileFlutter.flushSyncData() to manage synchronization manually |
syncPageSize | enum | No | Sets the number of entries per synchronization request. SyncPageSize.mini = 5 entries, SyncPageSize.medium = 10 entries, SyncPageSize.large = 50 entries, default SyncPageSize.medium |
customSyncPageSize | number | No | Sets the number of entries per synchronization request. Range [5,) Note: The larger the number of entries per request, the greater the computational resources consumed by data synchronization |
syncSleepTime | number | No | Sets the interval between synchronization requests. Range [0,5000], default not set |
globalContext | object | No | Adds custom tags. Refer to here for rules on adding tags |
serviceName | String | No | Service name |
enableLimitWithDbSize | boolean | No | Enables limiting data size using the database, default 100MB, unit Byte. Larger databases mean higher disk pressure, disabled by default. Note: After enabling, the Log configuration logCacheLimitCount and RUM configuration rumCacheLimitCount will no longer take effect. Supported in SDK 0.5.3-pre.2 and above versions |
dbCacheLimit | number | No | DB cache limit size. Range [30MB,), default 100MB, unit byte, supported in SDK 0.5.3-pre.2 and above versions |
dbCacheDiscard | string | No | Sets the database data discard policy. Discard strategy: FTDBCacheDiscard.discard discards new data (default), FTDBCacheDiscard.discardOldest discards old data. Supported in SDK 0.5.3-pre.2 and above versions |
compressIntakeRequests | boolean | No | Compresses uploaded synchronization data using deflate, defaults to off in versions 0.5.3-pre.2 and above |
enableDataIntegerCompatible | boolean | No | Recommended to enable when coexisting with web data. This setting addresses storage compatibility issues with web data types. Enabled by default in versions 0.5.4-pre.1 and above |
dataModifier | Map |
No | Modifies individual fields |
lineDataModifier | Map |
No | Modifies entire lines of data |
RUM Configuration¶
Field | Type | Required | Description |
---|---|---|---|
androidAppId | String | Yes | app_id, applied for in the Application Access Monitoring Console |
iOSAppId | String | Yes | app_id, applied for in the Application Access Monitoring Console |
sampleRate | double | No | Sampling rate, range [0,1], 0 means no sampling, 1 means full sampling, default value is 1. Scope is all View, Action, LongTask, Error data under the same session_id |
sessionOnErrorSampleRate | double | No | Sets error sampling rate. When a session is not sampled by setSamplingRate, if an error occurs during the session, data from the minute before the error can be collected. Range [0,1], 0 means no collection, 1 means full collection, default value is 0. Scope is 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 achieved by modifying HttpOverrides.global . If your project has customization requirements in this area, you need to inherit FTHttpOverrides . |
enableNativeUserAction | bool | No | Whether to track Native Action , native system Button click events, application startup events. Default is false |
enableNativeUserView | bool | No | Whether to perform automatic tracking of Native View . Pure Flutter applications are advised to disable this, default is false |
enableNativeUserViewInFragment | bool | No | Whether to automatically track page data of Native Fragment type. Default is false. Only supported on Android |
enableNativeUserResource | bool | No | Whether to perform automatic tracking of Native Resource . Pure Flutter applications are advised to disable this, default is false |
enableAppUIBlock | bool | No | Whether to perform automatic tracking of Native Freeze . Default is false |
nativeUiBlockDurationMS | int | No | Whether to set the time range for Native Freeze . Range [100,), unit milliseconds. iOS default is 250ms, Android default is 1000ms |
enableTrackNativeAppANR | bool | No | Whether to enable monitoring of Native ANR . Default is false |
enableTrackNativeCrash | bool | No | Whether to enable monitoring of Android Java Crash and OC/C/C++ crashes. Default is false |
errorMonitorType | enum ErrorMonitorType | No | Sets auxiliary monitoring information, adds additional monitoring data to the RUM Error data. ErrorMonitorType.battery indicates battery level, ErrorMonitorType.memory indicates memory usage, ErrorMonitorType.cpu indicates CPU utilization. Default is disabled |
deviceMetricsMonitorType | enum DeviceMetricsMonitorType | No | Adds monitoring data during the View lifecycle. DeviceMetricsMonitorType.battery (Android only) monitors maximum current output for the current page, DeviceMetricsMonitorType.memory monitors memory usage of the current application, DeviceMetricsMonitorType.cpu monitors CPU fluctuation counts, DeviceMetricsMonitorType.fps monitors screen frame rate. Default is disabled |
detectFrequency | enum DetectFrequency | No | Performance monitoring sampling cycle for views. Default is DetectFrequency.normal |
globalContext | Map | No | Custom global parameters |
rumCacheDiscard | enum | No | Discard strategy: FTRUMCacheDiscard.discard discards new data (default), FTRUMCacheDiscard.discardOldest discards old data |
rumCacheLimitCount | number | No | Local cache maximum RUM entry count limit [10_000,), default 100_000 |
isInTakeUrl | callBack | No | Sets conditions for filtering Resource entries. Default no filtering |
Adding Custom Tags¶
Static Usage¶
- Split the original main.dart into two parts: one for main(), and one for the App() MaterialApp component;
- Create corresponding entry files for each environment, such as: main_prod.dart, main_gray.dart, etc.;
- Configure custom tags in the corresponding environment files. 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¶
- Through file-type data storage, for example using the
shared_preferences
library'sSharedPreferences
, configure the use of theSDK
and add code to retrieve 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 methods 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 tracking functionality).
- When users add custom tags via globalContext that conflict with SDK's own tags, the SDK's tags will override the user's settings. It is recommended to prefix tag names with the project abbreviation, e.g.,
df_tag_name
. Keys used in the project can be referenced in the source code.
Log Configuration¶
Field | Type | Required | Description |
---|---|---|---|
sampleRate | double | No | Sampling rate, range [0,1], 0 means no sampling, 1 means full sampling, default value is 1. |
enableLinkRumData | bool | No | Whether to associate with RUM |
enableCustomLog | bool | No | Whether to enable custom logs |
logLevelFilters | List |
No | Log level filter |
logCacheLimitCount | int | No | Local cache maximum log entry count limit [1000,), more logs mean greater disk cache pressure, default 5000 |
discardStrategy | enum FTLogCacheDiscard | No | Sets the log discard rule after reaching the limit. Default FTLogCacheDiscard.discard , discard discards appended data, discardOldest discards old data |
Trace Configuration¶
Field | Type | Required | Description |
---|---|---|---|
sampleRate | double | No | Sampling rate, range [0,1], 0 means no sampling, 1 means full sampling, default value is 1. |
traceType | enum TraceType | No | Trace type, default TraceType.ddTrace . |
enableLinkRUMData | bool | No | Whether to associate with RUM data, default false . |
enableAutoTrace | bool | No | Whether to add Trace Header to http requests, default false . This is implemented by modifying HttpOverrides.global . If your project requires modifications in this aspect, you need to inherit FTHttpOverrides |
enableNativeAutoTrace | bool | No | Whether to enable native network auto-tracing for iOS NSURLSession , Android OKhttp , default false . |
RUM User Data Tracking¶
Action¶
Usage Method¶
/// Add action
/// [actionName] action name
/// [actionType] action type
/// [property] additional property parameter (optional)
Future<void> startAction(String actionName, String actionType,
{Map<String, String>? property})
Example Code¶
View¶
Auto Collection¶
- Method 1: Add
FTRouteObserver
toMaterialApp.navigatorObservers
, set up pages needing navigation inMaterialApp.routes
. Thekey
inroutes
becomes 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 navigating via routing
FTRouteObserver(),
],
routes: <String, WidgetBuilder>{
// Set Route navigation
'logging': (BuildContext context) => Logging(),
'rum': (BuildContext context) => RUM(),
'tracing_custom': (BuildContext context) => CustomTracing(),
'tracing_auto': (BuildContext context) => AutoTracing(),
},
);
}
}
// Navigate this way; the page name here is logging
Navigator.pushNamed(context, "logging");
- Method 2: Add
FTRouteObserver
toMaterialApp.navigatorObservers
, create a customFTMaterialPageRoute
to parse the page name from the widget class name. The widget class name becomes the page name (view_name
). See example here.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeRoute(),
navigatorObservers: [
// RUM View: Monitor page lifecycle when navigating via routing
FTRouteObserver(),
],
);
}
}
// The page name here is NoRouteNamePage
Navigator.of(context).push(FTMaterialPageRoute(builder: (context) =>
new NoRouteNamePage()
- Method 3: Add
FTRouteObserver
toMaterialApp.navigatorObservers
, customize theRouteSettings.name
attribute in theRoute
type page. The collection logic ofFTRouteObserver
will prioritize fetching the assigned value ofRouteSettings.name
. This method also applies to Dialog type pages, such asshowDialog()
,showTimePicker()
etc.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeRoute(),
navigatorObservers: [
// RUM View: Monitor page lifecycle when navigating via routing
FTRouteObserver(),
],
);
}
}
// The page name here is "RouteSettingName"
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => new NoRouteNamePage(),
settings: RouteSettings(name: "RouteSettingName"))
-
All three methods above can be mixed within a single project.
-
Sleep and Wake Event Collection For versions below 0.5.1-pre.1, if you need to collect application sleep and wake behaviors, add the following code:
class _HomeState extends State<HomeRoute> {
@override
void initState(){
// Add application sleep and wake listener
FTLifeRecycleHandler().initObserver();
}
@override
void dispose(){
// Remove application sleep and wake listener
FTLifeRecycleHandler().removeObserver();
}
}
Auto Collection Filter¶
Only supported in versions 0.5.0-pre.1 and above.
FTRouteObserver
MaterialApp(
navigatorObservers: [
// RUM View: routeFilter filters out pages that do not need to 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 callback method, judgment can be made based on entering and previous route situations, returns true means filtering the qualified data, vice versa means no filtering |
FTDialogRouteFilterObserver
Targeted filtering for DialogRoute
type pages, such as showDialog()
, showTimePicker()
etc.
MaterialApp(
navigatorObservers: [
// RUM View filters DialogRoute type components
FTDialogRouteFilterObserver(filterOnlyNoSettingName: true)
])
// This Dialog will be collected under the premise that filterOnlyNoSettingName is true.
// view_name is "About"
showAboutDialog(
context: context, routeSettings: RouteSettings(name: "About"));
Field | Type | Required | Description |
---|---|---|---|
filterOnlyNoSettingName | bool | No | Filters only Route pages where RouteSettings.name is null |
Custom View¶
Usage Method¶
/// View creation, this method needs to be called before [starView], currently not available in Flutter routes
/// [viewName] Interface name
/// [duration]
Future<void> createView(String viewName, int duration)
/// Start view
/// [viewName] Interface name
/// [viewReferer] Previous interface name
/// [property] Additional property parameter (optional)
Future<void> starView(String viewName, {Map<String, String>? property})
/// End view
/// [property] Additional property parameter (optional)
Future<void> stopView({Map<String, String>? property})
Example Code¶
FTRUMManager().createView("Current Page Name", 100000000)
FTRUMManager().starView("Current Page Name");
FTRUMManager().stopView();
Error¶
Auto 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 Method¶
/// Add custom error
/// [stack] Stack log
/// [message] Error message
/// [appState] Application state
/// [errorType] Custom errorType
/// [property] Additional property parameter (optional)
Future<void> addCustomError(String stack, String message,
{Map<String, String>? property, String? errorType})
Example Code¶
Resource¶
Auto Collection¶
Achieved by enabling enableUserResource
through configuration FTRUMManager().setConfig
.
Custom Resource¶
Usage Method¶
/// Start resource request
/// [key] Unique ID
/// [property] Additional property parameter (optional)
Future<void> startResource(String key, {Map<String, String>? property})
/// End resource request
/// [key] Unique ID
/// [property] Additional property parameter (optional)
Future<void> stopResource(String key, {Map<String, String>? property})
/// Send resource data metrics
/// [key] Unique ID
/// [url] Request address
/// [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})
Example Code¶
/// 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 http and dio libraries, refer to example.
Logger Log Output¶
Custom Logs¶
Currently, the log content is limited to 30 KB, and any excess will be truncated.
Usage Method¶
/// Output log
/// [content] Log content
/// [status] Log status
/// [property] Additional property parameter (optional)
Future<void> logging(String content, FTLogStatus status, {Map<String, String>? property})
Example Code¶
Log Levels¶
Method Name | Meaning |
---|---|
FTLogStatus.info | Informational |
FTLogStatus.warning | Warning |
FTLogStatus.error | Error |
FTLogStatus.critical | Critical |
FTLogStatus.ok | Recovery |
Tracer Network Trace¶
Auto Collection¶
Enabled by turning on enableAutoTrace
via configuration FTTracer().setConfig
.
Custom Tracer¶
Usage Method¶
/// Get trace HTTP request headers
/// [key] Unique ID
/// [url] Request address
///
Future<Map<String, String>> getTraceHeader(String url, {String? key})
Example Code¶
/// 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 http and dio libraries, refer to example.
Binding and Unbinding User Information¶
FTMobileFlutter¶
Usage Method¶
/// Bind user
///
/// [userid] User ID
/// [userName] Username
/// [userEmail] User email
/// [userExt] Extended data
static Future<void> bindRUMUserData(String userId,
{String? userName, String? userEmail, Map<String, String>? ext})
/// Unbind user
static Future<void> unbindRUMUserData()
Example Code¶
Manually Synchronize Data¶
FTMobileFlutter¶
WebView Data Monitoring¶
WebView data monitoring requires integrating the Web Monitoring SDK on the accessed page.
Data Masking¶
If you want to fully mask fields, it is recommended to use dataModifier
, which performs better. For detailed rule replacement, recommend using lineDataModifier
await FTMobileFlutter.sdkConfig(
dataModifier: {"device_uuid":"xxx"},
lineDataModifier: {"view":{"view_name":"xxx"}}
)
Hybrid Development with Native and Flutter¶
If your project is developed natively and some pages or business processes are implemented using Flutter, the installation and initialization configuration methods for the SDK are as follows:
- Installation: The installation method remains unchanged
- Initialization: Please refer to the iOS SDK Initialization Configuration and Android SDK Initialization Configuration to perform initialization configuration in the native project
- Flutter Configuration:
- View, Resource, Error configurations are the same as those for pure Flutter projects
- Flutter Resource and Trace auto-capture uses the following configuration: