LogDrop
Sdk

LogDrop Flutter SDK

The LogDrop Flutter SDK provides a Dart-friendly API on top of the native iOS and Android SDKs.

It lets you initialize LogDrop once in your Flutter app and automatically forward logs, crashes and network data to the LogDrop backend.


1. SDK Installation

1.1. Add the package to pubspec.yaml

In your Flutter project, open pubspec.yaml and add:
dependencies:
  flutter:
    sdk: flutter

  logdrop_flutter_sdk: ^3.0.4
The latest version is resolved automatically at build time.
Then run:
flutter pub get

2. Initialization

Initialize LogDrop in your main() method using LogDropFlutter.init after ensuring bindings are initialized.

In your lib/main.dart:
import 'package:flutter/material.dart';
import 'package:logdrop_flutter_sdk/logdrop_flutter.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await LogDropFlutter.init();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'LogDrop SDK Demo',
      debugShowCheckedModeBanner: false,
      home: const Scaffold(
        body: Center(
          child: Text('Hello LogDrop'),
        ),
      ),
    );
  }
}

3. Native Configuration

Since the Flutter SDK bridges to native layers, you must configure your LogDrop Application ID (App ID / API Key) and settings (like base URL) in your native iOS and Android projects.

3.1. Android Setup

Edit your project-level android/build.gradle:

buildscript {
    repositories {
        google()
        mavenCentral()
        maven(uri("https://artifactory.logdrop.io/repository/logdrop-gradle-plugin/"))
    }
    dependencies {
        classpath("io.logdrop.gradle:plugin:1.1.1")
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url "https://artifactory.logdrop.io/repository/android-logdrop-sdk/" }
        maven { url "https://artifactory.logdrop.io/repository/logdrop-gradle-plugin/" }
    }
}

Edit your app-level android/app/build.gradle:

apply plugin: "io.logdrop.gradle.plugin"
// or
plugins {
    ... // other plugins
    id("io.logdrop.gradle.plugin")
}
android {
    ...
    defaultConfig {
        buildConfigField("String", "LOGDROP_BASE_URL", "YOUR_SERVER_URL")
        buildConfigField("String", "LOGDROP_APP_ID", "YOUR_APP_ID")
        buildConfigField("boolean", "LOGDROP_LOGCAT_ENABLED", "true")
    }
}

Create a file named logdrop-services.json under the android/app folder:

{
  "base_url": "https://server.logdrop.io",
  "projects": {
    "YOUR_APP_PACKAGE_NAME": {
      "app_id": "YOUR_APP_ID"
    }
  }
}

Edit your YourApp.kt file in the Android module of your Flutter project as follows:

import android.app.Application
import com.logdrop_flutter_sdk.LogDropFlutter

class YourApp : Application() {
    override fun onCreate() {
        super.onCreate()
        LogDropFlutter.initLogDrop(
            logcatEnabled = BuildConfig.LOGDROP_LOGCAT_ENABLED,
            appId = BuildConfig.LOGDROP_APP_ID,
            baseUrl = BuildConfig.LOGDROP_BASE_URL,
            context = this.applicationContext
        )
    }
}

3.2. iOS Setup

Add the following keys to your Runner/Info.plist file:

<key>LogDropBaseUrl</key>
<string>YOUR_API_URL</string>

<key>LogDropAppId</key>
<string>YOUR_APP_ID</string>

<key>LogDropLoggingEnabled</key>
<true/>

Update AppDelegate.swift:

import Flutter
import UIKit
import logdrop_flutter_sdk

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
      let infoDict = Bundle.main.infoDictionary
      let appId = infoDict?["LogDropAppId"] as? String ?? ""
      let baseUrl = infoDict?["LogDropBaseUrl"] as? String ?? ""
      let loggingEnabled = infoDict?["LogDropLoggingEnabled"] as? Bool ?? true
     
      LogDropFlutter.initialize(
        appId: appId,
        baseUrl: baseUrl,
        loggingEnabled: loggingEnabled,
        pushAppGroupSuiteName: "group.com.yourcompany.yourapp"
      )

      GeneratedPluginRegistrant.register(with: self)
      return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

4. Crash Tracking

The Flutter SDK automatically registers crash tracking listeners for both Flutter framework errors and uncaught Dart errors under the hood during LogDropFlutter.init(...).

The native iOS and Android layers handle platform-level native crashes (see their respective SDK docs for symbol/mapping files upload).

Make sure the native LogDrop SDK is initialized before these handlers through the Android and iOS native setup described above.


5. Logging Methods

Use the LogDrop logging API from anywhere in your Flutter code:
// Without flow
LogDrop.logError(tag: 'PaymentActivity', message: 'Payment failed');        // Error
LogDrop.logWarning(tag: 'Network', message: 'Slow network');                 // Warning
LogDrop.logInfo(tag: 'Auth', message: 'User logged in');                     // Info
LogDrop.logDebug(tag: 'Parser', message: 'Parsing response');                // Debug

// With flow
final flow = LogFlow(
  name: 'SuccessTransaction',
  id: transactionId,
  customAttributes: {
    'amount': '149.90',
    'currency': 'TRY',
  },
);

LogDrop.logInfo(tag: 'Transaction', message: 'Transaction completed successfully.', logFlow: flow);

Logs are stored locally on the device first.

To trigger an upload to the server:

LogDrop.sendLogs();
  • Schedules a background job that runs approximately 60 seconds later.
  • If a job is already scheduled or running, additional calls do not create new jobs.

6. LogFlow

LogFlow lets you group related logs under a specific business flow or transaction.

final flow = LogFlow(
  name: 'CartCheckout',
  id: checkoutId,
  customAttributes: {
    'itemsCount': '3',
    'total': '249.90',
    'currency': 'TRY',
  },
);

LogDrop.logInfo(tag: 'Checkout', message: 'Checkout started.', logFlow: flow);
LogDrop.logInfo(tag: 'Checkout', message: 'Payment step opened.', logFlow: flow);
LogDrop.logInfo(tag: 'Checkout', message: 'Checkout completed.', logFlow: flow);

In the LogDrop panel you can filter and analyze logs by flowName and flowId.


7. Push Notification Integration

Keep your application's existing push provider integration. Forward its token and messages identified by LogDrop.isLogDropPush(...) to the Flutter bridge.

7.1. Android with FlutterFire Messaging

Register the background handler at top level. Forward LogDrop messages from both foreground and background handlers, and register both the current FCM token and future token updates.

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:logdrop_flutter_sdk/logdropsdk.dart';

Map<String, String> _logDropData(RemoteMessage message) =>
    message.data.map((key, value) => MapEntry(key, value.toString()));

@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();

  final data = _logDropData(message);
  if (await LogDrop.isLogDropPush(data)) {
    LogDrop.onRemoteMessageReceived(data);
  }
}

Future<void> registerLogDropPushHandlers() async {
  FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);

  final messaging = FirebaseMessaging.instance;
  final token = await messaging.getToken();
  if (token != null) {
    LogDrop.onNewFcmPushToken(token);
  }

  messaging.onTokenRefresh.listen(LogDrop.onNewFcmPushToken);
  FirebaseMessaging.onMessage.listen((message) async {
    final data = _logDropData(message);
    if (await LogDrop.isLogDropPush(data)) {
      LogDrop.onRemoteMessageReceived(data);
    }
  });
}

Call registerLogDropPushHandlers() during startup after initializing LogDrop and Firebase. For HMS, forward the provider token with LogDrop.onNewHmsPushToken(token) and use the same message forwarding methods.

7.2. iOS APNs Forwarding

Forward the APNs token and remote notifications from AppDelegate.swift:

override func application(
    _ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
    LogDropFlutter.onNewApnsToken(apnsToken: deviceToken)
}

override func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
    LogDropFlutter.onRemoteMessageReceived(userInfo: userInfo)
    completionHandler(.newData)
}

7.3. iOS Notification Service Extension

Complete the iOS Notification Service Extension and App Group setup. Use the same App Group identifier in LogDropFlutter.initialize(...), both target entitlements, and the extension configuration:

import LogDropSDK

final class NotificationService: LogDropNotificationServiceExtension {
    override func logDropConfiguration() -> LogDropConfig? {
        LogDropConfig.Builder()
            .setAppId("YOUR_APP_ID")
            .setBaseUrl("YOUR_BASE_URL") // Optional: for self-hosted LogDrop
            .setPushAppGroupSuiteName("group.com.yourcompany.yourapp")
            .build()
    }
}

7.4. Show a Pending Push Action

When the application UI becomes active, ask LogDrop to present a queued in-app popup or internal-browser action:

final result = await LogDrop.showPendingPushPopup();
// shown / nothingToShow / notInitialized

Keep routing links through your existing Flutter router. After the application receives and resolves a URL, forward that URL once to LogDrop:

await LogDrop.trackDeepLink(uri.toString());

Use the same call for cold-start and already-running link deliveries.


9. User Identification (UUID Customization)

By default, logs are tied to a device identifier. To associate logs with your own user ID:

LogDrop.userUpdate('user-1234@example.com');
You can use any stable, unique identifier, for example:
  • Email address

  • Internal customer ID

  • Other application-specific user ID

    All subsequent logs will be associated with this user in the LogDrop panel.


10. Custom Events

Use custom events to track application-specific actions that are not covered by LogDrop's built-in events. Send a stable event name together with optional JSON-compatible properties.

10.1. Track a Custom Event

await LogDrop.trackCustomEvent('purchase_completed', {
  'coupon_code': 'SUMMER10',
  'amount': 49.9,
  'item_count': 2,
  'is_first_order': true,
});

Properties are optional. To track an event without properties:

await LogDrop.trackCustomEvent('onboarding_completed');

Provide only the readable event name and properties. Event and property codes shown in the panel are managed by LogDrop.

10.2. Supported Property Values

Custom properties may contain strings, numbers, booleans, null, nested maps, and lists. Map keys must be strings.

await LogDrop.trackCustomEvent('product_viewed', {
  'sku': 'LD-100',
  'viewed_at': DateTime.now().toUtc().toIso8601String(),
  'product': {
    'name': 'LogDrop Hoodie',
    'price': 79.95,
  },
  'categories': ['merch', 'hoodies'],
  'variants': [
    {'color': 'black', 'size': 'M'},
    {'color': 'green', 'size': 'L'},
  ],
  'campaign': null,
});

Flutter's method channel accepts codec-compatible values. Convert DateTime instances to ISO-8601 strings before passing them. Nested maps and lists remain structured values.

10.3. Naming and Property Types

  • Use stable and consistent names. For example, do not alternate between purchase_completed and purchaseCompleted for the same event.
  • Do not put IDs, timestamps, or other dynamic values in the event name. Add them as properties instead.
  • Do not use the name of a LogDrop default event for a custom event.
  • Keep each property's type stable. For example, always send amount as a number rather than sometimes sending a number and sometimes a string.
  • Reusing the same event name updates the same custom event schema. Changing the name creates a different custom event.

If a property is later sent with a different type, LogDrop keeps the event but marks the property as a type conflict in the panel. Type conflict tracking helps find integration mistakes; it should not be used as a replacement for a stable event contract.

10.4. Limits and Validation

  • Event names can contain up to 128 characters.
  • Property names can contain up to 128 characters.
  • One event can contain up to 100 top-level properties.
  • The complete properties JSON can contain up to 16 KB in UTF-8, including property names, primitive values, nested objects, and arrays.
  • Event names and property names cannot be empty.
  • Property values must be JSON-compatible.

If validation fails, the native SDK rejects the complete event and writes the reason to the SDK log. Names, properties, and payloads are never silently truncated.

10.5. Panel Behavior

No custom event definition is required before tracking. After the first valid event reaches LogDrop, the Events page shows the event, its generated code, observed properties, property types, and any type conflicts. You can then use the event in event details and Event Analytics funnels.


11. Custom User Attributes

Custom user attributes let you attach stable profile information to the current LogDrop device and use it for segment-based push targeting in the panel. Call these APIs after LogDropFlutter.init() has completed.

11.1. Set or Update Attributes

await LogDrop.setCustomAttributes({
  'plan_type': 'premium',
  'country': 'TR',
  'preferred_language': 'tr',
});

The update is an additive upsert. New keys are added and existing keys receive the new value, while attributes omitted from the call remain unchanged. Valid changes are persisted by the native SDK and synchronized with LogDrop automatically.

11.2. Remove an Attribute

await LogDrop.removeCustomAttribute('plan_type');

Removing a key also synchronizes the deletion with LogDrop, so the previous value is no longer available for future segment targeting.

11.3. Limits and Validation

  • Attributes must be provided as a Map<String, String>.
  • A device can have up to 30 custom attributes in total.
  • Keys must contain only letters, numbers, underscores (_), or hyphens (-).
  • Keys can contain up to 64 characters.
  • Values must be non-blank strings containing up to 256 characters.
  • If one entry in a call is invalid, the native SDK rejects the complete update.
  • If the merged result would exceed 30 keys, the previous attribute set remains unchanged.

Custom user attributes are different from LogFlow.customAttributes. User attributes describe a device for audience targeting, while flow attributes add context only to the logs recorded with that LogFlow.

11.4. Use Attributes in the Panel

After the device synchronizes, open Push Notification → Send Push, select Segment under Recipients, and build a filter with the attribute key. See Segment Targeting with Custom Attributes.


12. Troubleshooting

  • Verify that the native iOS and Android SDK configurations are correctly set up.
  • Ensure LogDropFlutter.init(...) is called early in app startup.
  • Check that the logdrop_flutter_sdk package version matches the native SDK versions if you override them.
  • Confirm that custom event names do not match LogDrop default event names.
  • Check native SDK logs when a custom event is missing; invalid names, properties, or oversized payloads are rejected locally.
  • Check native SDK logs when custom user attributes are missing; invalid updates and updates exceeding 30 keys are rejected locally.
  • For FCM integration:
    • Confirm that Firebase is initialized before using FirebaseMessaging.
    • Make sure background handlers are registered as top-level functions.
    • Verify that isLogDropPush, onNewFcmPushToken, and onRemoteMessageReceived are being called.

On this page