Skip to content

Flutter Application Integration


Prerequisites

Note: If you have enabled the RUM Headless service, the prerequisites have been automatically configured for you. You can directly proceed with the application integration.

Application Integration

The current Flutter version only supports Android and iOS platforms. Log in to the Guance console, enter the Synthetic Tests page, and click the top-left [Create] to start creating a new application.

Installation

Pub.Dev: ft_mobile_agent_flutter

Source Code Address: https://github.com/GuanceCloud/datakit-flutter

Demo Address: https://github.com/GuanceCloud/datakit-flutter/example

In the project directory, run the Flutter command in the terminal:

 $ flutter pub add ft_mobile_agent_flutter

This will add the following line to the pubspec.yaml file (and implicitly run flutter pub get):

dependencies:
  ft_mobile_agent_flutter: [lastest_version]

  # For compatibility with Flutter 2.0, use the reference method below
  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 Gradle Plugin ft-plugin to collect App launch events and Android Native related events (page transitions, click events, Native network requests, WebView data).
  • Customize Application and declare it in AndroidMainifest.xml, as shown below.
import io.flutter.app.FlutterApplication

/**
* To track the number of launches and launch times, 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();
    // Deployment environment, 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 URL address for local environment deployment (Datakit), example: http://10.0.0.1:9529, default port is 9529. The device installing the SDK must be able to access this address. Note: Choose either datakitUrl or datawayUrl.
datawayUrl String Yes Public Dataway reporting URL address, obtained from the [Synthetic Tests] application, example: https://open.dataway.url. The device installing the SDK must be able to access this address. Note: Choose either datakitUrl or datawayUrl.
cliToken String Yes Authentication token, needs to be configured simultaneously with datawayUrl.
debug bool No Whether to allow log printing, default is false.
env String No Environment configuration, default is prod. Any character string, single-word suggestions like test are recommended.
envType enum EnvType No Environment configuration, default is EnvType.prod. Note: Only one of env or envType needs to be configured.
autoSync bool No Whether to automatically synchronize data to the server after collection, default is true. When set to 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 is SyncPageSize.medium.
customSyncPageSize number No Set the number of entries for sync requests. Range [5,), note: the larger the request entry count, the more computational resources required for data synchronization.
syncSleepTime number No Set the inter-sync pause time. Range [0,5000], default is not set.
globalContext object No Add custom tags. Refer to the rules here.
serviceName String No Service name.
enableLimitWithDbSize boolean No Enable limiting data size using db, default is 100MB, unit Byte. Larger databases increase disk pressure, default is disabled.
Note: After enabling, the logCacheLimitCount setting for Logs and the rumCacheLimitCount setting for RUM will be invalid. Supported by SDK versions 0.5.3-pre.2 and above.
dbCacheLimit number No DB cache limit size. Range [30MB,), default is 100MB, unit byte. Supported by SDK versions 0.5.3-pre.2 and above.
dbCacheDiscard string No Set the data discard rule for the database.
Discard strategy: FTDBCacheDiscard.discard discards new data (default), FTDBCacheDiscard.discardOldest discards old data. Supported by SDK versions 0.5.3-pre.2 and above.
compressIntakeRequests boolean No Compress uploaded sync data using deflate compression. Supported by SDK versions 0.5.3-pre.2 and above, default is disabled.
enableDataIntegerCompatible boolean No In cases where coexistence with web data is needed, it is recommended to enable this. This configuration handles storage compatibility issues with web data types. Default is enabled for SDK versions 0.5.4-pre.1 and above.

RUM Configuration

 await FTRUMManager().setConfig(
        androidAppId: appAndroidId, 
        iOSAppId: appIOSId,
    );
Field Type Required Description
androidAppId String Yes app_id, applied in the Synthetic Tests console.
iOSAppId String Yes app_id, applied in the Synthetic Tests console.
sampleRate double No Sampling rate, range [0,1], 0 means no sampling, 1 means full sampling, default is 1. Scope applies to all View, Action, LongTask, Error data under the same session_id.
enableUserResource bool No Whether to automatically capture Resource data via HTTP, default is false. This is achieved by modifying HttpOverrides.global. If there are customization requirements in the project, inherit FTHttpOverrides.
enableNativeUserAction bool No Whether to track Native Action, such as clicks on native system Button and app launch events, default is false.
enableNativeUserView bool No Whether to perform automatic tracking of Native View. It is recommended to turn this off for pure Flutter applications, default is false.
enableNativeUserResource bool No Whether to perform automatic tracking of Native Resource. It is recommended to turn this off for pure Flutter applications, 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. Default is 250ms for iOS, 1000ms for Android.
enableTrackNativeAppANR bool No Whether to enable Native ANR monitoring, 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 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 is not enabled.
deviceMetricsMonitorType enum DeviceMetricsMonitorType No During the View lifecycle, add monitoring data, DeviceMetricsMonitorType.battery (only Android) monitors the highest output current for the current page, DeviceMetricsMonitorType.memory monitors the current application memory usage, DeviceMetricsMonitorType.cpu monitors CPU jumps, DeviceMetricsMonitorType.fps monitors screen frame rate, default is not enabled.
detectFrequency enum DetectFrequency No Sampling period for performance monitoring of 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 Maximum number of cached RUM entries locally, range [10_000,), default is 100_000.
isInTakeUrl callBack No Set conditions to filter Resources, default does not filter.

Adding Custom Tags

Static Usage
  1. Split the original main.dart into two parts, one being main(), the other being 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. Use file-type data storage, such as the shared_preferences library SharedPreferences, configure SDK usage, and add code to obtain tag data during configuration.
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 file data anywhere.
 static Future<void> setDynamicParams(String value) async{
    final prefs = await SharedPreferences.getInstance();
    prefs.setString(CUSTOM_DYNAMIC_TAG, value);
  }
  1. Restart the application finally.

Note:

  1. Special key: track_id (used for tracking functionality).
  2. When users add custom tags through globalContext that conflict with SDK-owned tags, the SDK's tags will override user settings. It is recommended to prefix tag names with an abbreviation of the project, such as df_tag_name. Project key values can be queried in 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 is 1.
enableLinkRumData bool No Whether to link with RUM data.
enableCustomLog bool No Whether to enable custom logs.
logLevelFilters List No Log level filtering.
logCacheLimitCount int No Maximum number of cached log entries locally, range [1000,), larger logs mean greater disk cache pressure, default is 5000.
discardStrategy enum FTLogCacheDiscard No Set the log discard rule when the limit is reached. Default is 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 is 1.
traceType enum TraceType No Trace type, default is TraceType.ddTrace.
enableLinkRUMData bool No Whether to link with RUM data, default is false.
enableAutoTrace bool No Whether to add Trace Header in http requests, default is false. This is achieved by modifying HttpOverrides.global. If there are modification requirements in the project, 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 Method

  /// Add action
  /// [actionName] action name
  /// [actionType] action type
  /// [property] Additional attribute 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 in MaterialApp.routes, 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 during route navigation
        FTRouteObserver(),
      ],
      routes: <String, WidgetBuilder>{
        // Set Route for navigation
        'logging': (BuildContext context) => Logging(),
        'rum': (BuildContext context) => RUM(),
        'tracing_custom': (BuildContext context) => CustomTracing(),
        'tracing_auto': (BuildContext context) => AutoTracing(),
      },
    );
  }
}

// Navigate to the page named "logging" using this method
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). Refer to the example here.
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeRoute(),
      navigatorObservers: [
        // RUM View: Monitor page lifecycle during route navigation
        FTRouteObserver(),
      ],
    );
  }
}

// Here the "Page Name" is "NoRouteNamePage"
Navigator.of(context).push(FTMaterialPageRoute(builder: (context) => 
    new NoRouteNamePage()
  • Method 3: Add FTRouteObserver to MaterialApp.navigatorObservers, customize the RouteSettings.name attribute in Route type pages. FTRouteObserver's collection logic will prioritize obtaining the value assigned to 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 during route navigation
        FTRouteObserver(),
      ],
    );
  }
}

// Here the "Page Name" is "RouteSettingName"
Navigator.of(context).push(
          MaterialPageRoute(
              builder: (context) => new NoRouteNamePage(),
              settings: RouteSettings(name: "RouteSettingName"))
  • All three methods can be used together in one project.

  • Sleep and Wake Events Collection For versions below 0.5.1-pre.1, if you need to collect sleep and wake behaviors of the app, add the following code:

class _HomeState extends State<HomeRoute> {

    @override
    void initState(){

        // Add app sleep and wake listener
        FTLifeRecycleHandler().initObserver();
    }

    @override
    void dispose(){

        // Remove app sleep and wake listener
        FTLifeRecycleHandler().removeObserver();
    }
}

Automatic Collection Filtering

Only supported for versions 0.5.0-pre.1 and above

FTRouteObserver

MaterialApp(
  navigatorObservers: [
        // RUM View: Filter out pages that do not need to be monitored using routeFilter
         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, which can determine based on the current and previous route situations. Returning true represents filtering out data that meets the condition, otherwise it does not filter.

FTDialogRouteFilterObserver

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

MaterialApp(
  navigatorObservers: [
    // RUM View filters components of type DialogRoute
    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 out Routes where RouteSettings.name is null

Custom View

Usage Method
  /// View creation, this method must be called before [starView], currently not available in Flutter routes
  /// [viewName] Interface name
  /// [duration]
  Future<void> createView(String viewName, int duration)

  /// View start
  /// [viewName] Interface name
  /// [viewReferer] Previous interface name
  /// [property] Additional attribute parameters (optional)
  Future<void> starView(String viewName, {Map<String, String>? property})

  /// View end
  /// [property] Additional attribute 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 capturing
    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 attribute 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 via the configuration FTRUMManager().setConfig.

Custom Resource

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

  /// End resource request
  /// [key] Unique id
  /// [property] Additional attribute parameters (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})
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,
      );
    }
  }

Using the http library and dio library, refer to the example.

Logger Log Printing

Custom Logs

Currently, log content is limited to 30 KB, exceeding characters will be truncated.

Usage Method

  /// Output log
  ///[content] Log content
  ///[status] Log status
  ///[property] Additional attribute 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 Information
FTLogStatus.warning Warning
FTLogStatus.error Error
FTLogStatus.critical Critical
FTLogStatus.ok Recovery

Automatic Collection

Enable enableAutoTrace via the configuration FTTracer().setConfig.

Custom Tracer

Usage Method

  /// Get trace http request header data
  /// [key] Unique id
  /// [url] Request address
  ///
  Future<Map<String, String>> getTraceHeader(String url, {String? key})

Code Example

/// Using httpClient    
void httpClientGetHttp() async {
    var url = 'http://www.google.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;
        });
      }
    }
  }

Using the http library and dio library, refer to the 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()

Code Example

 FTMobileFlutter.bindUser("flutterUser");

 FTMobileFlutter.unbindUser();

Active Synchronization of Data

FTMobileFlutter

Usage Method

/// Sync data immediately
static Future<void> flushSyncData())

Usage Example

FTMobileFlutter.flushSyncData();

WebView Data Monitoring

To monitor WebView data, integrate the Web Monitoring SDK in the WebView accessed pages.

Native and Flutter Hybrid Development

If your project is natively developed with some pages or business processes implemented in Flutter, follow these steps for SDK installation and initialization:

  • Installation: Installation remains unchanged.
  • Initialization: Refer to iOS SDK Initialization Configuration and Android SDK Initialization Configuration to initialize within the native project.
  • Flutter Configuration:
    • View, Resource, Error use the same configuration method as pure Flutter projects.
    • Use the following configuration for automatic collection of Flutter Resource and Trace.
          // Set traceHeader 0.5.3-pre.2 support
          FTHttpOverrideConfig.global.traceHeader = true;   
          // Set collection Resource data 0.5.3-pre.2 support
          FTHttpOverrideConfig.global.traceResource = true; 
      

Android

iOS

Common Issues

Feedback

Is this page helpful? ×