apptics_flutter 0.0.14 copy "apptics_flutter: ^0.0.14" to clipboard
apptics_flutter: ^0.0.14 copied to clipboard

Flutter plugin for Apptics, wrapper around Apptics Native iOS and Android SDK. Supports features likes in-app updates, in-app events, screens and sessions.

Apptics Flutter #

Flutter plugin for Apptics, a wrapper around the native Apptics iOS and Android SDKs. Apptics is Zoho's analytics and app-management platform.

This plugin exposes the following feature modules:

  • Core analytics — event, screen, session and user tracking
  • Privacy / tracking controls — granular consent states (PII / usage / crash)
  • In-app ratings — store review prompts, custom rating flows
  • Remote configuration — server-driven config params and conditions
  • In-app updates — flexible / immediate / force update prompts (native or custom UI)
  • Crash tracking — fatal & non-fatal exceptions, ANR detection (Android), custom properties
  • Feedback — feedback & bug-report screens, shake-to-feedback, logs & diagnostics
  • Push notifications — foreground/background message, click and action handling
  • API call tracking — network performance metrics (success rate, latency, status codes)

ℹ️ Push notifications and API tracking each have a dedicated companion guide: PUSH_NOTIFICATION_README.md and API_TRACKING_README.md. This README links to them from the relevant sections below.


Table of contents #


Compatibility #

Requirement Version
Flutter SDK >= 3.29.0
Dart SDK >= 2.18.4 < 4.0.0
Android minSdkVersion 23
Android compileSdk 34
Android Java / Kotlin Java 17 / Kotlin JVM target 17
iOS deployment target 9.0 (as declared in the podspec; raise it if a native pod requires more)
Native Android SDK com.zoho.apptics:* 0.3.16 (analytics, crash-tracker, appupdates, ratings, feedback, rc, pns)
Native iOS pods Apptics-SDK and modules 3.3.13 (notification extensions 3.3.12)
Apptics Gradle plugin com.zoho.apptics:apptics-plugin:0.2.5

The plugin version (pubspec.yaml) is 0.0.14. The native SDK numbers above are not the plugin version — see CHANGELOG.md for the plugin/native version mapping per release.


Installation #

Add the plugin to your app's pubspec.yaml:

dependencies:
  apptics_flutter: ^0.0.14

Then run:

flutter pub get

You must also complete the native platform setup below — the plugin does nothing until the native Apptics SDK is configured with your app's credentials.


Platform setup #

Android setup #

  1. Register your app and download apptics-config.json from the Apptics web console.

  2. Place apptics-config.json in the app/ directory (the root of your Android app module).

  3. In your project-level build.gradle, add the Apptics Gradle plugin classpath and the Zoho Maven repository:

    buildscript {
        repositories {
            maven { url "https://maven.zohodl.com/" }
        }
        dependencies {
            classpath 'com.zoho.apptics:apptics-plugin:0.2.5'
        }
    }
    
    allprojects {
        repositories {
            maven { url "https://maven.zohodl.com/" }
        }
    }
    
  4. (Optional) Configure the Apptics Gradle plugin in your app-level build.gradle — for example to control build-time logging:

    apptics {
        showLogs = ["default": true]
    }
    
  5. Minimum supported Android SDK is 23. The plugin compiles against compileSdk 34 with Java 17.

The plugin's AndroidManifest.xml automatically registers the push-notification broadcast receiver (AppticsPushNotificationReceiver) and its intent filters — you don't need to declare them yourself. See Push notifications.

iOS setup #

  1. Register your app and download apptics-config.plist from the Apptics web console.

  2. Move apptics-config.plist into the Xcode project root and add it to the main target.

  3. Add the Apptics pre-build script. This script is mandatory — it registers the app version with the Apptics server, writes app information into apptics-config.plist (consumed by the SDK at runtime), and optionally uploads dSYMs.

    Option A — via the Podfile (recommended):

    source 'https://github.com/zoho/Apptics.git'
    source 'https://github.com/CocoaPods/Specs.git'
    
    target 'Runner' do
      # Registers the app version, uploads dSYMs, and injects app info into apptics-config.plist.
      script_phase :name => 'Apptics pre build',
        :script => 'sh "./Pods/Apptics-SDK/scripts/run" --upload-symbols-for-configurations="Release, AppStore"',
        :execution_position => :before_compile
    end
    

    Option B — manually in Xcode:

    • In the project navigator, select your project, then the target you want to modify.

    • Open the Build Phases tab, click + → New Run Script Phase, and rename it Apptics pre build.

    • Expand the phase and paste the script into the Shell text field:

      sh "./Pods/Apptics-SDK/scripts/run" --upload-symbols-for-configurations="Release, AppStore"
      
    • Phase ordering matters: Apptics pre build must be above Compile Sources, and Copy Bundle Resources must be below Compile Sources.

  4. Run pod install from your ios/ directory and build the Xcode project once to verify the setup.

Build-script flags

run --upload-symbols-for-configurations="Release, AppStore" \
    --config-file-path="YOUR_PATH/apptics-config.plist" \
    --app-group-identifier="group.com.company.application"   # optional
  • --config-file-path — pass your config file path if it is not in the project root.
  • --app-group-identifier — required if you have enabled an App Group for your target.
  • --upload-symbols-for-configurations — comma-separated build configuration names (e.g. Debug, Release) for which dSYMs are uploaded automatically during App Store submission via CI/CD, without an interactive prompt.

For multi-project setups, see the iOS integration guide.

Configuration files, API keys & data centers #

Apptics has no separate login/authentication call in code. Instead, each app is identified to the Apptics backend by the credentials embedded in its native config file, which is generated per-app in the web console:

Platform File Key field Server / data-center field
Android apptics-config.json apid / app key (ZAK) service URL (e.g. https://sdk-apptics.zoho.in)
iOS apptics-config.plist API key server URL (e.g. https://apptics.zoho.in)

The server URL inside the config file selects the data center your data is sent to (for example zoho.in for India, zoho.com.au for Australia, localzoho.com for local development). You don't configure the data center in Dart — it is baked into the config file you download. Always download the config file from the console region that matches your Apptics account.

Keep these config files out of public version control if your app is open-source — they contain the key that authorizes data ingestion for your app.

Multi-environment / multi-product configuration #

The example app demonstrates how to keep several Apptics configurations side by side (the example/ios folder ships four plists — apptics-config.plist, apptics-config-au.plist, apptics-config-local.plist, apptics-config-old.plist — pointing at different data centers/bundle IDs).

Important: shipping multiple config files does not by itself switch environments. At runtime the iOS SDK reads the single file named by the AP_INFOPLIST_FILE key in Info.plist (the example points it at apptics-config.plist). There is no build phase that swaps the file automatically. To switch environments or products cleanly:

  • iOS — create separate Xcode build configurations / schemes, and either set a different AP_INFOPLIST_FILE value per configuration or pass --config-file-path to the pre-build script for each configuration. Use distinct PRODUCT_BUNDLE_IDENTIFIER values per scheme if you ship multiple products. (The example's notification-service and notification-content extensions use the <bundle-id>.notificationservice / <bundle-id>.notificationcontent suffixes — keep that prefix relationship when changing bundle IDs.)
  • Android — use Gradle product flavors / build variants and generate the matching apptics-config.json per variant before the build. (The example app currently uses a single apptics-config.json and does not define flavors.)

When you change the bundle ID / application ID, you must regenerate the config file from the console for that ID — the key is bound to the bundle/application identifier.


Getting started #

AppticsFlutter is the entry point for core analytics. It is a singleton — use AppticsFlutter.instance (or construct AppticsFlutter(), which returns the same shared instance).

import 'package:apptics_flutter/apptics_flutter.dart';

// Preferred: shared singleton.
final apptics = AppticsFlutter.instance;

// Equivalent — AppticsFlutter() also resolves to the singleton.
final apptics2 = AppticsFlutter();

Each feature module is its own singleton imported from its own file (e.g. AppticsCrashTracker.instance, AppticsFeedback.instance). Imports are shown in each section below.


Core analytics #

Event tracking #

Track a configured event with an optional properties map.

// Without properties
AppticsFlutter.instance.addEvent("eventName", "eventGroupName");

// With properties
AppticsFlutter.instance.addEvent(
  "eventName",
  "eventGroupName",
  properties: {"propKey": "propValue"},
);

Signature: Future<void> addEvent(String? event, String? group, {Map<String, dynamic>? properties}).

Predefined events #

The plugin ships a DefinedEvents class of standard event/group name constants (app lifecycle, deep links, notifications, user lifecycle, OS, network, theme/orientation, etc.). Use these for consistency with Apptics' built-in reporting.

import 'package:apptics_flutter/defined_events.dart';

AppticsFlutter.instance.addEvent(
  DefinedEvents.AP_USER_LOGIN,        // event name
  DefinedEvents.AP_USER_LIFE_CYCLE,   // group name
);

Available groups include AP_APP_LIFE_CYCLE, AP_APPLICATION, AP_USER_LIFE_CYCLE, AP_OS, and AP_OTHERS, each with related event constants (for example AP_APP_OPEN, AP_DEEP_LINK_OPEN, AP_NOTIFICATION_RECEIVE, AP_USER_SIGNUP, AP_NETWORK_REACHABILITY_CHANGE, AP_SWITCH_THEME_DARK). See lib/defined_events.dart for the complete list.

Screen tracking #

// Call when the screen appears
AppticsFlutter.instance.screenAttached("screenName");

// Call when the screen disappears
AppticsFlutter.instance.screenDetached("screenName");

User identification #

Tie a user ID to your stats (events, screens, crashes). User association honors the current tracking state: when the state is a "WithoutPII" variant, stats are not associated with the user ID.

⚠️ To protect user privacy, never pass personally identifiable information as the user ID.

// Set the current user (optional group ID)
AppticsFlutter.instance.setUser("userId");
AppticsFlutter.instance.setUser("userId", "groupId");

// Remove / log out the current user (optional group ID)
AppticsFlutter.instance.removeUser("userId");
AppticsFlutter.instance.removeUser("userId", "groupId");

// Check whether a user is currently logged in
bool? loggedIn = await AppticsFlutter.instance.isUserLoggedIn();

User properties #

Attach properties (email, name, plan type, etc.) to the current user. Properties such as email are also used for Feedback user identification.

import 'package:apptics_flutter/apptics_user_property.dart';

final props = AppticsUserPropertyBuilder()
    .setFirstName("Ada")
    .setLastName("Lovelace")
    .setEmailAddress("ada@example.com")
    .setCompanyName("Analytical Engines")
    .setPlanType("enterprise")
    .addStringProperty("custom_key", "value")   // arbitrary custom props
    .addNumberProperty("seats", 42)
    .addBooleanProperty("beta_user", true)
    .build();

await AppticsFlutter.instance.setUserWithProperty(
  "userId",
  groupId: "groupId",      // optional
  props: props,            // optional
);

// Read back the current user's properties (requires Android SDK 0.3.9+)
Map<String, dynamic>? current = await AppticsFlutter.instance.getUserProperties();

AppticsUserPropertyBuilder provides typed setters (setFirstName, setLastName, setCompanyName, setContactNumber, setEmailAddress, setCountry, setRegion, setCity, setGeoLocation, setGender, setPlanType, setTimezone, setLanguage, setEngagementScore, setDateOfBirth) plus generic addStringProperty, addNumberProperty, and addBooleanProperty for custom keys.

Tracking settings & privacy #

Apptics offers seven tracking states to control usage and crash tracking. The Dart enum values are:

TrackingState value Numeric Description
usageAndCrashTrackingWithPII 1 Usage + crash, associated with user ID
onlyUsageTrackingWithPII 2 Usage only, with user ID
onlyCrashTrackingWithPII 3 Crash only, with user ID
usageAndCrashTrackingWithoutPII 4 Default — usage + crash, no user association
onlyUsageTrackingWithoutPII 5 Usage only, no user association
onlyCrashTrackingWithoutPII 6 Crash only, no user association
noTracking -1 Tracking disabled

usageAndCrashTrackingWithoutPII is the default state out of the box.

  • Usage tracking covers events, APIs, screens and sessions.
  • Crash tracking covers unhandled exceptions.
  • PII refers to the value you set via setUser.
import 'package:apptics_flutter/apptics_flutter_util.dart'; // for TrackingState

// Change the tracking state (note the lower-camelCase enum identifiers)
await AppticsFlutter.instance.setTrackingState(TrackingState.onlyCrashTrackingWithPII);

// Read the current tracking state
TrackingState? state = await AppticsFlutter.instance.getTrackingState();

You can also show Apptics' built-in privacy UI:

// Show the built-in privacy options popup
await AppticsFlutter.instance.presentPrivacyReviewPopup();

// Open the screen where the user can change analytics privacy settings
await AppticsFlutter.instance.openPrivacySettings();

Language & flush #

// Set the language used by the Analytics SDK (iOS only; no-op on Android).
// Falls back to the default language if the code is not in the resource bundle.
await AppticsFlutter.instance.setDefaultLanguage("en");

// Force-send all buffered stats (events, screens, sessions) to the server.
await AppticsFlutter.instance.flush();

In-app ratings #

import 'package:apptics_flutter/rateus/apptics_in_app_rating.dart';

Show the default rating popup #

Call checkForRatingPop after the theme and navigator are set up.

AppticsInAppRating.instance.checkForRatingPop(context);

// Enable the "Send Feedback" action in the popup:
AppticsInAppRating.instance.checkForRatingPop(context, isFeedbackEnabled: true);

Signature: Future<void> checkForRatingPop(BuildContext context, {bool isFeedbackEnabled = false}).

The popup appears automatically according to the criteria configured in the web console and has two or three actions:

  • Rate in Play Store — redirects to the store for ratings/reviews.
  • Later — dismisses and re-prompts after a deferral period.
  • Send Feedback — only shown when the Apptics Feedback SDK is integrated and isFeedbackEnabled is true; opens the Feedback screen. Ensure the Apptics theme is set, or the app will crash.

The default rating popup does not currently support Material 3.

Tuning the prompt cadence #

If the user chooses Later, the next prompt is deferred by 10 days by default. After three consecutive Later choices, the popup is suppressed until the criteria for that app version are reconfigured in the console.

// Defer period in days (getter + setter method)
await AppticsInAppRating.instance.setDaysBeforeShowingPopupAgain(15);
int days = AppticsInAppRating.instance.daysBeforeShowingPopupAgain;

// Maximum number of times to show the popup
await AppticsInAppRating.instance.setMaxTimesToShowPopup(5);
int max = AppticsInAppRating.instance.maxTimesToShowPopup;

These are read via getters but set via methods (setDaysBeforeShowingPopupAgain / setMaxTimesToShowPopup) — they are not assignable fields.

Build your own rating UI #

import 'package:apptics_flutter/rateus/apptics_in_app_rating.dart';
import 'package:apptics_flutter/rateus/popup_action.dart';

// Returns a criteriaId (int) when a prompt is due, or null otherwise. Runs asynchronously.
int? criteriaId = await AppticsInAppRating.instance.getCriteriaId();

if (criteriaId != null) {
  // ...show your custom UI, then report the action the user took:
  await AppticsInAppRating.instance.sentStats(criteriaId, PopupAction.LATER_CLICKED);
}

sentStats takes positional arguments: Future<void> sentStats(int criteriaId, PopupAction popupAction). PopupAction values are RATE_IN_STORE_CLICKED, SEND_FEEDBACK_CLICKED, and LATER_CLICKED.

Other helpers: isAppticsFeedbackModuleAvailable(), openFeedback(), openPlayStore(), updateRatingShown(), setDisableAutoPromptOnFulFillingCriteria(bool).

Store in-app review API #

AppticsInAppRating.instance.setShowStoreAlertOnFulFillingCriteria(true);

When enabled (and you use checkForRatingPop), Google Play's native in-app review flow is presented automatically once the configured criterion is met. The Play review API has no callback indicating whether the UI was shown, and its quotas are undocumented — so once invoked, Apptics defers the next call by daysBeforeShowingPopupAgain.


Remote configuration #

import 'package:apptics_flutter/remoteconfig/apptics_remote_config.dart';

Configure remote-config params and conditions in the web console, then fetch values at runtime:

Future<void> loadConfig() async {
  String? color = await AppticsRemoteConfig.instance.getStringValue('color');
  if (color != null) {
    // use the value
  }
}

Signature: Future<String?> getStringValue(String key, {bool coldFetch = false, bool fallbackWithOfflineValue = false})

  • key — the param name configured in the web console.
  • coldFetch — by default getStringValue uses a cache to avoid frequent network calls. When true, it ignores the cache and fetches from the network. Network fetches are throttled to 3 calls per minute; beyond that the method returns null (or the offline value if fallbackWithOfflineValue is set).
  • fallbackWithOfflineValue — when true, returns the previously fetched value on network failure.

Custom condition criteria #

AppticsRemoteConfig.instance.setCustomConditionValue("ConditionKey", "Criteria");

To clear cached remote-config state, use AppticsRemoteConfig.instance.hardReset().


In-app updates #

import 'package:apptics_flutter/appupdate/apptics_in_app_update.dart';

Show the default update popup #

Call checkAndUpdateAlert after the theme and navigator are set up.

AppticsInAppUpdates.instance.checkAndUpdateAlert(context);

The default update popup does not currently support Material 3.

Build your own update flow #

getInAppUpdateData() returns a Map<String, dynamic>? you can use to render a custom popup, or null when no update is available. It takes no arguments.

Map<String, dynamic>? data = await AppticsInAppUpdates.instance.getInAppUpdateData();

Determine the response type from the category value: 1 = normal update data, 2 = unsupported-OS popup data. The action methods you call from your custom UI are onClickUpdate, onClickReminder, onClickIgnore, onClickNonSupportAlert, and onSendImpressionStatus(String updateId).

Keys for a normal-update (category == 1) map:

Key Meaning
updateid ID of the specific update configuration
currentversion App version installed on the device
featureTitle Title/heading for the version alert
features Update features / "what's new"
remindMeLaterText Localized text for the "Remind Me Later" action
updateNowText Localized text for the "Update" action
neverAgainText Localized text for the "Ignore" action
option 1 = flexible, 2 = immediate, 3 = force update flow
reminderDays Days before re-prompting after "Remind Me Later" (console-configured)
forceInDays Force-update window (console-configured)
alertType 0 = native UI, 1 = custom UI, 2 = Android in-app updates
customStoreUrl Alternate store URL to redirect to on "Update/Download"

Keys for an unsupported-OS (category == 2) map:

Key Meaning
title Popup title
description Popup description
continueBtTxt Button text
alertType 0 = do nothing, 1 = install later, 2 = freeze
updateid ID of the specific update configuration

See the in-app updates guide.


Crash tracking #

import 'package:apptics_flutter/crash_tracker/apptics_crash_tracker.dart';

Fatal crashes (automatic) #

Enable automatic crash tracking once, early in your app:

AppticsCrashTracker.instance.autoCrashTracker();

For finer control you can also forward Flutter framework errors yourself:

FlutterError.onError = (details) {
  AppticsCrashTracker.instance.sendFlutterException(details);          // isFatal: true by default
};

// Or report an arbitrary exception:
AppticsCrashTracker.instance.sendException(error, stackTrace, isFatal: true);

Non-fatal exceptions #

try {
  int k = (1 ~/ 0);
} catch (e, s) {
  AppticsCrashTracker.instance.sendNonFatalException(e, s);
}

// From a FlutterErrorDetails:
AppticsCrashTracker.instance.sendFlutterNonFatalException(details);

Custom properties #

Attach custom properties to both fatal and non-fatal events:

await AppticsCrashTracker.instance.setCrashCustomProperty({
  "name": "ABCD",
  "domains": ["Data Science", "Mobile", "Web"],
});

getLastCrashInfo() and showLastSessionCrashedPopup() are Android-only. On iOS, getLastCrashInfo() returns null.

// Retrieve the previous crash as a stringified JSON object (Android).
String? lastCrash = await AppticsCrashTracker.instance.getLastCrashInfo();

// Show a consent popup to send the previous session's crash info (Android).
AppticsCrashTracker.instance.showLastSessionCrashedPopup();

Sample getLastCrashInfo() JSON:

{
  "issuename": "divide by zero",
  "message": "java.lang.ArithmeticException: divide by zero\n\tat com.zoho.apptics.MainActivity.onCreate$lambda$2(MainActivity.kt:42)",
  "networkstatus": 0,
  "serviceprovider": "T-Mobile",
  "orientation": 0,
  "batterystatus": 100,
  "ram": "2.9 GB",
  "rom": "5.8 GB",
  "sessionstarttime": 1711445408267,
  "customproperties": {},
  "screenname": "com.zoho.apptics.MainActivity",
  "happenedat": 1711445420908,
  "happenedcount": 1,
  "errortype": "native"
}

ANR detection (Android) #

Application-Not-Responding detection is available on Android:

await AppticsCrashTracker.instance.enableANR();
await AppticsCrashTracker.instance.disableANR();
bool? enabled = await AppticsCrashTracker.instance.isANREnabled();

setAttemptInstantSync(bool) requests an immediate upload attempt for crashes.


Feedback #

import 'package:apptics_flutter/feedback/apptics_feedback.dart';

Open the feedback / bug-report screens #

AppticsFeedback.instance.openFeedback();  // open the feedback screen
AppticsFeedback.instance.reportBug();     // take + annotate a screenshot to report a bug

Shake-to-feedback #

Shake-to-feedback lets users shake the device to open the feedback screen. Enabled by default.

AppticsFeedback.instance.enableShakeForFeedback();
AppticsFeedback.instance.disableShakeForFeedback();
bool? enabled = await AppticsFeedback.instance.isShakeForFeedbackEnabled();

Anonymous user alerts #

Collect feedback without requiring user identification. Enabled by default.

AppticsFeedback.instance.enableAnonymousUserAlert();
AppticsFeedback.instance.disableAnonymousUserAlert();
bool? enabled = await AppticsFeedback.instance.isAnonymousUserAlertEnabled();

Identify the feedback sender #

await AppticsFeedback.instance.setEmailId("user@example.com");

Send feedback / bug reports programmatically #

AppticsFeedback.instance.sendFeedback(
  "Great application",   // feedbackMessage
  true,                  // includeLogs
  true,                  // includeDiagnostics
  guestMailId: "user@example.com",        // optional
  forceToAnonymous: false,                 // optional
  attachmentsUri: [Uri.parse('file://screenshot.png')], // optional
);

AppticsFeedback.instance.sendBugReport(
  "Great app, but I found a bug!",
  true, true,
  guestMailId: "user@example.com",
  attachmentsUri: [Uri.parse('file://screenshot.png')],
);

Both methods share the signature (String feedbackMessage, bool includeLogs, bool includeDiagnostics, {String? guestMailId, bool forceToAnonymous = false, List<Uri>? attachmentsUri}).

Logs & diagnostics #

import 'package:apptics_flutter/feedback/apptics_logs.dart';
import 'package:apptics_flutter/feedback/apptics_log_type.dart'; // the Log enum

// Write a log line. Log enum values are lower-case: verbose, debug, info, warn, error.
AppticsLogs.instance.writeLog("Some log message", Log.debug);

// Or attach a log file instead.
AppticsLogs.instance.addLogFile(File("path/to/log.txt"));

// Add grouped key/value diagnostics.
AppticsLogs.instance.addDiagnosticsInfo("HEADING", "key", "value");

// Clear accumulated logs/diagnostics.
AppticsLogs.instance.resetLogsAndDiagnostics();

If both addLogFile and writeLog are used, the attached file takes priority. Only one log file is allowed per feedback and it must not exceed 1 MB.


Push notifications #

The plugin supports foreground/background message reception, notification clicks, and action-button clicks. Setup involves registering top-level @pragma('vm:entry-point') background handlers in main(), then calling AppticsPushNotification.initialize(...) with foreground handlers. On iOS you additionally call AppticsFlutter.instance.startService() and AppticsFlutter.instance.registerPushNotification() (both are no-ops on Android).

import 'package:apptics_flutter/push_notification/apptics_push_notification.dart';

@pragma('vm:entry-point')
Future<void> _onBackgroundMessage(Map<String, dynamic> message) async {
  // handle background message
}

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  AppticsPushNotification.setOnMessageHandlerListener(_onBackgroundMessage);
  runApp(const MyApp());
}

👉 Full setup, background processing, event types, and the API reference are in PUSH_NOTIFICATION_README.md.


API call tracking #

Report network requests to the Apptics dashboard as performance metrics (success rate, latency, status codes) per endpoint. Four integration strategies are provided:

Strategy Best for Setup
Auto tracking (HttpOverrides) Catching all dart:io traffic One line
Dio interceptor Projects using Dio Add interceptor
http.BaseClient wrapper Projects using the http package Swap client
Manual tracking Custom clients / non-standard setups Call per request
import 'package:apptics_flutter/api_tracker/apptics_api_tracker.dart';

void main() {
  AppticsApiTracker.instance.enableAutoTracking(); // captures all dart:io HTTP calls
  runApp(MyApp());
}

👉 Strategy details, the API reference, URL exclusion, normalization, and troubleshooting are in API_TRACKING_README.md.


Architecture #

The plugin follows the standard federated Flutter-plugin architecture with platform channels.

┌──────────────────────────────────────────────┐
│                Your Flutter app               │
└───────────────────────┬──────────────────────┘
                        │  public Dart APIs
        ┌───────────────┼───────────────────────────────┐
        │               │                               │
 AppticsFlutter   AppticsCrashTracker / Feedback /   AppticsApiTracker /
 (core)           InAppRating / InAppUpdates /        PushNotification
                  RemoteConfig
        └───────────────┴───────────────────────────────┘
                        │
        AppticsFlutterPlatform (platform interface)
                        │
        MethodChannelAppticsFlutter  ──  MethodChannel('apptics_flutter')
                        │                 + background channel
                        │                 'com.zoho.apptics.flutter/push_background'
        ┌───────────────┴───────────────┐
        │                               │
 Android plugin (Kotlin)         iOS plugin (Objective-C)
 AppticsFlutterPlugin.kt         AppticsFlutterPlugin.m
        │                               │
 Native Apptics Android SDK      Native Apptics iOS pods
 (com.zoho.apptics:* 0.3.16)     (Apptics-SDK 3.3.13, ...)

Key points:

  • Platform interface patternAppticsFlutterPlatform (abstract) defines every method; MethodChannelAppticsFlutter is the concrete implementation.

  • Single primary method channel — all native calls use MethodChannel('apptics_flutter'). Push notifications additionally use a background channel (com.zoho.apptics.flutter/push_background) for headless isolate execution.

  • Platform branching — many methods branch on Platform.isIOS / Platform.isAndroid. Notably, the method-channel layer sends snake_case argument keys to iOS and camelCase to Android.

  • Feature modules live under lib/:

    Path Module
    lib/apptics_flutter.dart Core analytics & user/privacy APIs
    lib/defined_events.dart Predefined event/group constants
    lib/apptics_user_property.dart AppticsUserProperty + builder
    lib/apptics_flutter_util.dart TrackingState and related enums
    lib/rateus/ In-app ratings
    lib/remoteconfig/ Remote configuration
    lib/appupdate/ In-app updates
    lib/crash_tracker/ Crash & ANR tracking
    lib/feedback/ Feedback, logs & diagnostics
    lib/push_notification/ Push notifications
    lib/api_tracker/ API call tracking

Running the example app #

The example/ app exercises every module and is the fastest way to see the plugin working.

cd example
flutter pub get

# iOS
cd ios && pod install && cd ..
flutter run -d ios

# Android
flutter run -d android

The example app's main screen wires up buttons for events, crashes, non-fatals, flush, feedback, in-app updates, login/logout, privacy settings, rate-us, and remote config, plus a dedicated API Tracking screen demonstrating the auto / HTTP-wrapper / manual strategies. The example depends on the http package (for the HTTP-wrapper demo); it does not use Dio.

Before running, ensure the native config files are present:

  • Androidapptics-config.json in example/android/app/
  • iOSapptics-config.plist referenced by AP_INFOPLIST_FILE in example/ios/Runner/Info.plist

Without these, native methods initialize but report no data.


Testing #

flutter analyze          # static analysis (uses flutter_lints)
flutter test             # unit tests
flutter test --coverage  # with coverage
dart format .            # formatting

The current Dart unit tests (test/apptics_flutter_test.dart, test/apptics_flutter_method_channel_test.dart) are minimal scaffolding: they verify the default platform instance and mock the method channel, but do not exercise real native behavior. Functional verification is done through the example app on real devices/simulators. This is a known gap — contributions adding behavioral tests are welcome.


Platform support matrix #

Most methods work on both platforms. The exceptions:

API iOS Android Notes
setDefaultLanguage(String) No-op on Android
getLastCrashInfo() Returns null on iOS
showLastSessionCrashedPopup() Android only
enableANR / disableANR / isANREnabled ANR is an Android concept
registerPushNotification() / startService() iOS-only wrappers (no-op on Android)

Troubleshooting #

  • No data appears in the dashboard. Confirm the config file is present and correct (apptics-config.json / apptics-config.plist), the data-center/server URL matches your account region, and call AppticsFlutter.instance.flush() to force an immediate sync.
  • iOS build fails / SDK reports it isn't configured. Verify the Apptics pre build script phase exists, is above Compile Sources, and that Copy Bundle Resources is below it. Re-run pod install.
  • Rating / update popup looks wrong or crashes when opening Feedback. The default popups don't support Material 3, and the Feedback action requires the Apptics theme to be set.
  • writeLog won't compile. The Log enum values are lower-case (Log.debug, not Log.DEBUG).
  • Rating prompt cadence won't set. daysBeforeShowingPopupAgain / maxTimesToShowPopup are getters; set them with setDaysBeforeShowingPopupAgain(int) / setMaxTimesToShowPopup(int).
  • Push notification background handlers never fire. Mark them with @pragma('vm:entry-point') and register them before calling AppticsPushNotification.initialize(...). See the push guide.
  • API calls tracked twice. Don't combine auto tracking with the Dio interceptor / HTTP wrapper for the same client. See the API-tracking guide.

For native-call debugging: iOS logs appear in the Xcode console; on Android use adb logcat | grep apptics.


Contributing & releasing #

  1. Run flutter analyze and flutter test before committing.
  2. When adding functionality, update all four layers: the platform interface (AppticsFlutterPlatformInterface), the method channel (MethodChannelAppticsFlutter), the public API class, and the native Android + iOS implementations.
  3. Bump the version in pubspec.yaml and the iOS podspec, and update CHANGELOG.md (including the native SDK versions used).
  4. Verify changes through the example app on both platforms.

Resources #

5
likes
140
points
271
downloads

Documentation

API reference

Publisher

verified publisherzoho.com

Weekly Downloads

Flutter plugin for Apptics, wrapper around Apptics Native iOS and Android SDK. Supports features likes in-app updates, in-app events, screens and sessions.

Homepage

License

MIT (license)

Dependencies

flutter, http, plugin_platform_interface

More

Packages that depend on apptics_flutter

Packages that implement apptics_flutter