Skip to content

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.

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:

 $ flutter pub add ft_mobile_agent_flutter

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:

import 'package:ft_mobile_agent_flutter/ft_mobile_agent_flutter.dart';

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 in AndroidMainifest.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

 await FTRUMManager().setConfig(
        androidAppId: appAndroidId, 
        iOSAppId: appIOSId,
    );
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
  1. Split the original main.dart into two parts: one for main(), and one for the App() MaterialApp component;
  2. Create corresponding entry files for each environment, such as: main_prod.dart, main_gray.dart, etc.;
  3. 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
  1. Through file-type data storage, for example using the shared_preferences library's SharedPreferences, configure the use of the SDK 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
    );
  1. 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);
  }
  1. Finally, restart the application.

Note:

  1. Special key: track_id (used for tracking functionality).
  2. 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

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

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

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

View

Auto Collection

  • Method 1: Add FTRouteObserver to MaterialApp.navigatorObservers, set up pages needing navigation 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 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 to MaterialApp.navigatorObservers, create a custom FTMaterialPageRoute 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 to MaterialApp.navigatorObservers, customize the RouteSettings.name attribute in the Route type page. The collection logic of FTRouteObserver will prioritize fetching the assigned value of RouteSettings.name. This method also applies to Dialog type pages, such as showDialog(), 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
 /// Custom error
 FTRUMManager().addCustomError("error stack", "error message");

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

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

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

 FTMobileFlutter.bindUser("flutterUser");

 FTMobileFlutter.unbindUser();

Manually Synchronize Data

FTMobileFlutter

FTMobileFlutter.flushSyncData();

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:
          // Set traceHeader support in version 0.5.3-pre.2
          FTHttpOverrideConfig.global.traceHeader = true;   
          // Enable Resource data capture in version 0.5.3-pre.2
          FTHttpOverrideConfig.global.traceResource = true; 
      

Android

iOS

Frequently Asked Questions

Feedback

Is this page helpful? ×