LogDrop Flutter SDK

Installation

Minimum Requirements:

  • Android 6.0
  • iOS 14.0
flutter pub add logdrop_flutter_sdk

Integration

Android

Edit your 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.2")
    }
}

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 android/app/build.gradle (App Level):

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
import org.json.JSONArray

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

iOS

Add the following keys to your 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 UserNotifications
import logdrop_flutter_sdk

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
      UNUserNotificationCenter.current().delegate = self

      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.your.app.identifier" // Optional: required for rich push in extensions
      )

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

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

  override func application(
      _ application: UIApplication,
      didFailToRegisterForRemoteNotificationsWithError error: Error
  ) {
      print("Failed to register for remote notifications: \(error)")
  }

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


Rich push App Group

LogDrop rich-push popup and internal-browser actions received by a Notification Service Extension must be shared with the main application:

  1. Add a Notification Service Extension target.
  2. Enable the App Groups capability on both Runner and the extension with the same identifier, such as group.your.app.identifier.
  3. Pass that exact identifier to pushAppGroupSuiteName in both the main app initialization above and the extension configuration below.
  4. Add LogDrop to the extension target in ios/Podfile, then run pod install:
target 'LogDropNotificationServiceExtension' do
  use_frameworks!
  pod 'LogDrop', '2.1.3'
end

Use LogDrop's service-extension base class:

import LogDropSDK

final class NotificationService: LogDropNotificationServiceExtension {
  override func logDropConfiguration() -> LogDropConfig? {
    LogDropConfig.Builder()
      .setBaseUrl("YOUR_BASE_URL")
      .setPushAppGroupSuiteName("group.your.app.identifier")
      .build()
  }
}

The suite name must match exactly across the app configuration, extension configuration, and both targets' App Group entitlements.

Usage

Initialize LogDrop in your main() function:

import 'package:flutter/material.dart';
import 'package:logdrop_flutter_sdk/logdrop_flutter.dart';
import 'package:your_app/main_app.dart';

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

Logging Messages

You can use the logging functions to send logs to the native side.
Each log function requires a tag and a message, and optionally a LogFlow object.


void main() {
  // Log an error
  LogDrop.logError(
    tag: "LoginScreen",
    message: "Login failed due to invalid credentials",
  );

  // Log a debug message
  LogDrop.logDebug(
    tag: "ApiClient",
    message: "Request sent to /users endpoint",
  );

  // Log an info message
  LogDrop.logInfo(
    tag: "PaymentFlow",
    message: "Payment initialized successfully",
  );

  // Log a warning message
  LogDrop.logWarning(
    tag: "ProfileUpdate",
    message: "Profile picture is too large, compressing...",
  );

  // Using LogFlow
  final flow = LogFlow(
    name: "Checkout",
    id: "flow-123",
    customAttributes: {
      "cartId": "cart-456",
      "userId": "user-789",
    },
  );

  LogDrop.logInfo(
    tag: "CheckoutScreen",
    message: "User started checkout flow",
    logFlow: flow,
  );
}


Push Notifications

⚠️ Before using these functions, make sure you have requested notification permission on the device (both Android and iOS require explicit permission).

These methods should be called inside the push notification callbacks of your app:

import 'package:logdrop_flutter_sdk/logdropsdk.dart';

Future<void> setupPushNotifications() async {
  // 1. Request notification permission from the user
  // (example for FlutterFire Messaging, adapt to your push SDK)
  await FirebaseMessaging.instance.requestPermission();

  // 2. Listen for new push tokens (FCM)
  FirebaseMessaging.instance.onTokenRefresh.listen((token) {
    LogDrop.onNewFcmPushToken(token);
  });

  // 3. Handle incoming push notifications
  FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
    final data = message.data.map((k, v) => MapEntry(k, v.toString()));
    if (await LogDrop.isLogDropPush(data)) {
      LogDrop.onRemoteMessageReceived(data);
    }
  });

  // (For Huawei devices, use HMS push SDK and call onNewHmsPushToken)
}

Background Messages

For handling push notifications when the app is in the background or terminated, register a background message handler:

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

  // On Android, FCM automatically displays notification payloads while the
  // app is backgrounded or terminated. Do not ask LogDrop to display the same
  // notification again. Data-only messages still continue below.
  if (message.notification != null) {
    return;
  }

  final data = message.data.map((k, v) => MapEntry(k, v.toString()));
  if (await LogDrop.isLogDropPush(data)) {
    LogDrop.onRemoteMessageReceived(data);
  }
}

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await LogDropFlutter.init();
  await Firebase.initializeApp();
  FirebaseMessaging.onBackgroundMessage(
    _firebaseMessagingBackgroundHandler,
  );
  runApp(const MyApp());
}

On Android, a message containing a notification payload is displayed automatically by FCM when the app is in the background or terminated. Calling LogDrop.onRemoteMessageReceived for that same message would create a second notification. The guard above leaves notification payloads to FCM and forwards only data-only background messages to LogDrop. Foreground messages should still be forwarded to LogDrop because FCM does not display them automatically in the foreground.

Checking LogDrop Push & Showing Rich Push Popup

// Check if a notification is from LogDrop
final isLogDrop = await LogDrop.isLogDropPush(data);

// Show any pending rich-push popup (e.g. after app comes to foreground)
final result = await LogDrop.showPendingPushPopup();
// result: LogDropPopupShowResult.shown / .nothingToShow / .notInitialized

iOS APNs Registration (Fetch Logs)

If you are targeting iOS, silent push notifications for log retrieval require the APNs device token. Since APNs tokens are binary Data and handled natively by iOS, you must register the token inside your native AppDelegate.swift (see the iOS integration section above).

Deep-link navigation in Flutter is handled by the application's routing layer. After the application receives a deep link, call the SDK's public method to track it:

import 'package:logdrop_flutter_sdk/logdropsdk.dart';

Future<void> handleDeepLink(Uri uri) async {
  await LogDrop.trackDeepLink(uri.toString());
}

Built-in deep linking is enabled by default in Flutter 3.27 and later. If your application uses Flutter's routing for deep links, no additional native callback bridge is required. Call LogDrop.trackDeepLink after the application receives the link and navigates to the appropriate route.

Updating user

void onUserLogin(String userId) {
  // 4. When the user logs in or updates
  LogDrop.userUpdate(userId);
}