Skip to content

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.

App Integration

Note

The current Flutter version only supports Android and iOS platforms.

  1. Navigate to RUM > Create Application > Android/iOS;
  2. Create two separate applications for Flutter Android and Flutter iOS to receive RUM data from the Android and iOS platforms respectively;
  3. Enter the corresponding application name and application ID for each platform's application;
  4. 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:

 $ flutter pub add ft_mobile_agent_flutter

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:

import 'package:ft_mobile_agent_flutter/ft_mobile_agent_flutter.dart';

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 Application and declare its use in AndroidMainifest.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

 await FTRUMManager().setConfig(
        androidAppId: appAndroidId,
        iOSAppId: appIOSId,
    );
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
  1. Split the original main.dart into 2 parts: one part is main(), the other part is the App() MaterialApp widget;
  2. Create entry files for various environments, e.g., main_prod.dart, main_gray.dart, etc.;
  3. 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
  1. Use a file-based data storage, e.g., the SharedPreferences library's SharedPreferences, to configure the SDK. 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
    );
  1. 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);
  }
  1. Finally, restart the application.

Note:

  1. Special key: track_id (used for tracing functionality).
  2. When a user adds a custom tag via globalContext that 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. The key values used in the project can be queried from the source code.

Log Configuration

 await FTLogger().logConfig(
   enableCustomLog: true
 );
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

await FTTracer().setConfig(
  enableLinkRUMData: true,
  enableAutoTrace: true,
);
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

FTRUMManager().startAction("action name", "action type");

View

Automatic Collection

  • Method 1: Add FTRouteObserver to MaterialApp.navigatorObservers, set the pages to navigate to in MaterialApp.routes. The key in routes 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 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 FTRouteObserver to MaterialApp.navigatorObservers, use a custom FTMaterialPageRoute to derive the page name from the widget's runtimeType. The widget class 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 FTRouteObserver to MaterialApp.navigatorObservers, customize the RouteSettings.name property in Route type pages. The collection logic of FTRouteObserver will prioritize the value assigned to RouteSettings.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
 ///Custom error
 FTRUMManager().addCustomError("error stack", "error message");

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

FTLogger().logging("info log content", FTLogStatus.info);

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

 FTMobileFlutter.bindUser("flutterUser");

 FTMobileFlutter.unbindUser();

Manual Data Synchronization

FTMobileFlutter

FTMobileFlutter.flushSyncData();

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:
          // Set traceHeader Supported in 0.5.3-pre.2
          FTHttpOverrideConfig.global.traceHeader = true;
          //Set collection of Resource data Supported in 0.5.3-pre.2
          FTHttpOverrideConfig.global.traceResource = true;
      

Android

iOS

FAQ

Feedback

Is this page helpful? ×