Skip to content

Flutter Application Integration


Prerequisites

Note: If you have enabled the RUM Headless service, the prerequisites have been automatically configured for you, and you can directly integrate the application.

Application Integration

Note

The current Flutter version only supports Android and iOS platforms.

  1. Go to User Access Monitoring > Create Application > Android/iOS;
  2. Create two applications for Flutter Android and Flutter iOS respectively to receive RUM data from Android and iOS platforms;
  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 following Flutter command in the project directory:

 $ flutter pub add ft_mobile_agent_flutter

This will add the following line to the pubspec.yaml file of the package (and run an implicit flutter pub get):

dependencies:
  ft_mobile_agent_flutter: [lastest_version]

  # For compatibility with Flutter 2.0, use the following reference
  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 Configuration for Android Integration

  • Configure the Gradle Plugin ft-plugin to collect App launch events and Android Native related events (page navigation, click events, Native network requests, WebView data)
  • Customize Application and declare it in AndroidMainifest.xml, as shown in the code below.
import io.flutter.app.FlutterApplication

/**
* If you need to count [Launch Count] and [Launch Time], add a custom Application here
*/
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 URL address for local environment deployment (Datakit) reporting, example: http://10.0.0.1:9529, default port 9529, the device installing the SDK must be able to access this address. Note: Only one of datakitUrl and datawayUrl should be configured
datawayUrl String Yes Public Dataway reporting URL address, obtained from the [User Access Monitoring] application, example: https://open.dataway.url, the device installing the SDK must be able to access this address. Note: Only one of datakitUrl and datawayUrl should be configured
cliToken String Yes Authentication token, must be configured with datawayUrl
debug bool No Set whether to allow logging, default false
env String No Environment configuration, default prod, any character, 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 should be configured
autoSync bool No Whether to automatically sync data to the server after collection, default true. When false, use FTMobileFlutter.flushSyncData() to manage data synchronization manually
syncPageSize enum No Set the number of entries for sync requests, SyncPageSize.mini 5 entries, SyncPageSize.medium 10 entries, SyncPageSize.large 50 entries, default SyncPageSize.medium
customSyncPageSize number No Set the number of entries for sync requests. Range [5,), note: the larger the number of entries, the more computational resources the data synchronization will occupy
syncSleepTime number No Set the sync interval time. Range [0,5000], default not set
globalContext object No Add custom tags. For adding rules, please refer to here
serviceName String No Service name
enableLimitWithDbSize boolean No Enable db size limit for data, default 100MB, unit Byte, the larger the database, the greater the disk pressure, default not enabled.
Note: After enabling, Log configuration logCacheLimitCount and RUM configuration rumCacheLimitCount will become invalid. SDK version 0.5.3-pre.2 and above supports this parameter
dbCacheLimit number No DB cache size limit. Range [30MB,), default 100MB, unit byte, SDK version 0.5.3-pre.2 and above supports this parameter
dbCacheDiscard string No Set data discard rules in the database.
Discard strategy: FTDBCacheDiscard.discard discard new data (default), FTDBCacheDiscard.discardOldest discard old data. SDK version 0.5.3-pre.2 and above supports this parameter
compressIntakeRequests boolean No Compress upload sync data with deflate, SDK version 0.5.3-pre.2 and above supports this parameter, default off
enableDataIntegerCompatible boolean No It is recommended to enable this when coexisting with web data. This configuration is used to handle web data type storage compatibility issues. SDK version 0.5.4-pre.1 and above enables this by default
dataModifier Map No Modify a single field.
lineDataModifier Map> No Modify a single line of data.

RUM Configuration

 await FTRUMManager().setConfig(
        androidAppId: appAndroidId, 
        iOSAppId: appIOSId,
    );
Field Type Required Description
androidAppId String Yes app_id, applied in the User Access Monitoring console
iOSAppId String Yes app_id, applied in the User Access Monitoring console
sampleRate double No Sampling rate, range [0,1], 0 means no collection, 1 means full collection, default is 1. Scope is all View, Action, LongTask, Error data under the same session_id
sessionOnErrorSampleRate double No Set error sampling rate, when the session is not sampled by setSamplingRate, if an error occurs during the session, data within 1 minute before the error can be collected, range [0,1], 0 means no collection, 1 means full collection, default is 0. Scope is all View, Action, LongTask, Error data under the same session_id
enableUserResource bool No Whether to enable automatic http Resource data collection, default is false, this is achieved by modifying HttpOverrides.global, if the project has customization needs in this area, inherit FTHttpOverrides.
enableNativeUserAction bool No Whether to track Native Action, native system Button click events, app launch events, default is false
enableNativeUserView bool No Whether to enable automatic Native View tracking, pure Flutter applications are recommended to turn this off, default is false
enableNativeUserViewInFragment bool No Whether to automatically track Native Fragment type page data, default is false. Only supports Android
enableNativeUserResource bool No Whether to enable automatic Native Resource tracking, pure Flutter applications are recommended to turn this off, default is false
enableAppUIBlock bool No Whether to enable automatic Native Freeze tracking, default is false
nativeUiBlockDurationMS int No Whether to set the time range for Native Freeze, range [100,), unit 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 Android Java Crash and OC/C/C++ crash monitoring, default is false
errorMonitorType enum ErrorMonitorType No Set auxiliary monitoring information, add additional monitoring data to RUM Error data, ErrorMonitorType.battery for battery level, ErrorMonitorType.memory for memory usage, ErrorMonitorType.cpu for CPU usage, default not enabled
deviceMetricsMonitorType enum DeviceMetricsMonitorType No Add monitoring data in the View cycle, DeviceMetricsMonitorType.battery (only Android) monitor the maximum output current of the current page, DeviceMetricsMonitorType.memory monitor the memory usage of the current application, DeviceMetricsMonitorType.cpu monitor CPU jumps, DeviceMetricsMonitorType.fps monitor screen frame rate, default not enabled
detectFrequency enum DetectFrequency No View performance monitoring sampling period, default is DetectFrequency.normal
globalContext Map No Custom global parameters
rumCacheDiscard enum No Discard strategy: FTRUMCacheDiscard.discard discard new data (default), FTRUMCacheDiscard.discardOldest discard old data
rumCacheLimitCount number No Local cache maximum RUM entry count limit [10_000,), default 100_000
isInTakeUrl callBack No Set conditions for filtering Resource, default no filtering

Add Custom Tags

Static Usage
  1. Split the original main.dart into two parts, one part is main(), the other part is App() MaterialApp component;
  2. Create 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, such as shared_preferences library SharedPreferences, configure the use of SDK, add code to get tag data at the configuration location.
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 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 function).
  2. When the user adds custom tags through globalContext that conflict with the SDK's own tags, the SDK's tags will override the user's settings. It is recommended to add a project abbreviation prefix to the tag name, such as df_tag_name. The key value used in the project can be queried 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 collection, 1 means full collection, default is 1.
enableLinkRumData bool No Whether to associate with RUM
enableCustomLog bool No Whether to enable custom logs
logLevelFilters List No Log level filtering
logCacheLimitCount int No Local cache maximum log entry count limit [1000,), the larger the log, the greater the disk cache pressure, default 5000
discardStrategy enum FTLogCacheDiscard No Set log discard rules when the limit is reached. Default FTLogCacheDiscard.discard, discard discard appended data, discardOldest discard 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 collection, 1 means full collection, default is 1.
traceType enum TraceType No Trace type, default TraceType.ddTrace. TraceType.ddTrace. TraceType.ddTrace, TraceType.zipkinMulti, TraceType.zipkinSingle, TraceType.traceparent, TraceType.skywalking,TraceType.jaeger
enableLinkRUMData bool No Whether to associate with RUM data, default false.
enableAutoTrace bool No Whether to add Trace Header in http requests, default false, this is achieved by modifying HttpOverrides.global, if the project has customization needs in this area, inherit FTHttpOverrides
enableNativeAutoTrace bool No Whether to enable native network auto-tracing iOS NSURLSession, Android OKhttp, default false.

RUM User Data Tracking

Action

Usage

  /// Add 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 MaterialApp.routes for pages to navigate to, the key in routes is 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(),
      },
    );
  }
}

//Navigate to the page in this way, the page name here is logging
Navigator.pushNamed(context, "logging");
  • Method 2: Add FTRouteObserver to MaterialApp.navigatorObservers, use a custom FTMaterialPageRoute to parse the page name from the widget's runtimeType, where the widget class name is 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 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, FTRouteObserver will prioritize the 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 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 in one project

  • Sleep and wake events collection For versions below 0.5.1-pre.1, if you need to collect application sleep and wake behavior, 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();
    }
}

Automatic Collection Filtering

Only supported in versions 0.5.0-pre.1 and above

FTRouteObserver

MaterialApp(
  navigatorObservers: [
        // RUM View: routeFilter filters 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 method callback, you can judge based on the current and previous route, return true to filter data that meets the conditions, otherwise do not filter

FTDialogRouteFilterObserver

Filter DialogRoute type pages, such as showDialog(),showTimePicker(), etc.

MaterialApp(
  navigatorObservers: [
    //RUM View filters DialogRoute type components
    FTDialogRouteFilterObserver(filterOnlyNoSettingName: true)
])

// The Dialog here will be collected if filterOnlyNoSettingName is true.
// view_name is "About"
showAboutDialog(
            context: context, routeSettings: RouteSettings(name: "About"));
Field Type Required Description
filterOnlyNoSettingName bool No Only filter Route pages with RouteSettings.name as null

Custom View

Usage
  /// View creation, this method needs to be called before [starView], currently not available in flutter route
  /// [viewName] page name
  /// [duration]
  Future<void> createView(String viewName, int duration)

  /// View start
  /// [viewName] page name
  /// [viewReferer] previous page name
  /// [property] additional property parameters (optional)
  Future<void> starView(String viewName, {Map<String, String>? property})

  /// View end
  /// [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 error capture
    FlutterError.onError = FTRUMManager().addFlutterError;
    runApp(MyApp());
  }, (Object error, StackTrace stack) {
    //Add Error data
    FTRUMManager().addError(error, stack);
  });

Custom Error

Usage
  /// Add 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 through configuration FTRUMManager().setConfig.

Custom Resource

Usage
  /// Start resource request
  /// [key] unique id
  /// [property] additional property parameters (optional)
  Future<void> startResource(String key, {Map<String, String>? property})

  /// End 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
/// Use 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 http library and dio library usage, refer to example.

Logger Logging

Custom Logs

Currently, the log content is limited to 30 KB, excess characters will be truncated

Usage

  /// Output 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 Trace

Automatic Collection

Enable enableAutoTrace through 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

/// Use 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 http library and dio library usage, refer to example.

User Information Binding and Unbinding

FTMobileFlutter

Usage

  /// Bind user
  ///
  ///[userid] user id
  ///[userName] user name
  ///[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()

Code Example

 FTMobileFlutter.bindUser("flutterUser");

 FTMobileFlutter.unbindUser();

Manual Data Sync

FTMobileFlutter

FTMobileFlutter.flushSyncData();

WebView Data Monitoring

WebView data monitoring requires integrating the Web Monitoring SDK in the WebView access page.

Data Masking

If you want to fully mask a field, it is recommended to use dataModifier, which performs better. If you need detailed rule replacement, it is recommended to use lineDataModifier

await FTMobileFlutter.sdkConfig(
    dataModifier: {"device_uuid":"xxx"},
    lineDataModifier: {"view":{"view_name":"xxx"}}
)

Native and Flutter Hybrid Development

If your project is native development, with some pages or business processes implemented using Flutter, the SDK installation and initialization configuration methods are as follows:

  • Installation: The installation method remains the same
  • Initialization: Refer to iOS SDK Initialization Configuration, Android SDK Initialization Configuration for initialization configuration in 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 version 0.5.3-pre.2
          FTHttpOverrideConfig.global.traceHeader = true;   
          //Set Resource data collection, supported in version 0.5.3-pre.2
          FTHttpOverrideConfig.global.traceResource = true; 
      

Android

iOS

Frequently Asked Questions

Feedback

Is this page helpful? ×