Flutter Diagnostic Logs & Export - scopetech/Mobile-Public GitHub Wiki

Flutter Diagnostic Logs & Export

Flutter equivalent of iOS Diagnostic Logs & Export. Last updated: 20 Jul, 2026.

The SDK, its callbacks, and the health check all run natively β€” so the logs they produce accumulate natively. The right approach in Flutter is to keep the log store and the report builder on the native side (exactly as the iOS guide describes) and expose a single channel method that builds the ZIP and returns its path. Dart then shares that file. Optionally, Dart-side logs can be forwarded into the same store so one bundle contains everything.

Important

Do not rebuild the log store in Dart. The most valuable entries (authorization, recorder state, permissions, trip callbacks, server errors) exist only natively. A Dart-only logger would miss all of them.

Table of contents

1. Principle

Dart (UI)                 MethodChannel              Native (iOS/Flutter)
─────────                 ─────────────              ───────────────────
Export button  ──exportLogs──────────────────────►  ReportManager builds ZIP
                                                     returns file path
share_plus     ◄──"…/SCPLog_20-Jul-2026.zip"───────

(optional) log(group,msg) ─────────────────────────► LogEventManager.saveLog(...)

The native LogEventManager and ReportManager are the same classes as the iOS/demo implementation β€” copy them into YOUR_FLUTTER_APPLICATION unchanged. Flutter only adds two channel methods.

2. Native side, the log store and export

Add LogEventManager.swift, ReportManager.swift, and the LogEvent Core Data model to ios/YOUR_FLUTTER_APPLICATION exactly as in the iOS guide Β§2 and Β§4. These capture SDK callbacks, health checks, and server errors natively.

Note

Set up the Core Data model the same way as iOS Β§2.1: add the .xcdatamodeld to the ** YOUR_FLUTTER_APPLICATION** target (Target Membership), set the entity's Codegen = Manual/None, and pass the model filename to NSPersistentContainer(name:). The same two mismatches β€” wrong container name, or codegen left on "Class Definition" β€” cause the identical crash/build errors here.

ReportComposer (email / share sheet UI) is not needed on Flutter β€” Dart handles sharing. You only need the store (LogEventManager) and the builder (ReportManager).

ReportManager depends on ZIPFoundation. A Flutter iOS app already uses CocoaPods, so add it to the YOUR_FLUTTER_APPLICATION target in the generated ios/Podfile, then run pod install (or cd ios && pod install):

target 'YOUR_FLUTTER_APPLICATION' do
  use_frameworks!
  # ... Flutter-generated lines ...

  pod 'ZIPFoundation'
end

3. Channel methods

Extend the same channel handler from Flutter Session & App Lifecycle Management Β§4 with an exportLogs method (and an optional log method):

case "exportLogs":
    // Builds the per-day ZIP with header, returns the file path for Dart to share.
    ReportManager.shared.createLogData { _ in
        reply(ReportManager.shared.dataURL?.path)   // nil if there were no logs
    }

case "log":
    // Optional: let Dart write into the same native store (see Β§5).
    guard let args = call.arguments as? [String: Any],
          let message = args["message"] as? String else {
        reply(FlutterError(code: "bad_args", message: "message required", details: nil)); return
    }
    logEvent(.Other, description: "[Flutter] \(message)")
    reply(nil)

4. Dart side, trigger and share

Add the bridge methods and share the returned file with share_plus:

class SmartDriveLogs {
  static const _ch = MethodChannel('scope/smartdrive');

  /// Builds the native log ZIP and returns its file path (null if empty).
  static Future<String?> exportLogs() async =>
      await _ch.invokeMethod<String>('exportLogs');
}
import 'package:share_plus/share_plus.dart';

Future<void> onExportLogsPressed() async {
  final path = await SmartDriveLogs.exportLogs();
  if (path == null) {
    // show "No logs to export yet"
    return;
  }
  await Share.shareXFiles([XFile(path)], subject: 'SmartDrive logs');
}

That is the whole flow: tap β†’ native builds the ZIP β†’ Dart shares it (email, Files, messaging, AirDrop).

5. Including your Dart logs, optional

If your Flutter layer keeps its own logs (network calls, navigation, errors), forward them into the native store so the exported ZIP is a single, complete timeline:

class SmartDriveLogs {
  // ...
  static Future<void> log(String message) =>
      _ch.invokeMethod('log', {'message': message});
}

// usage
SmartDriveLogs.log('Login screen shown, isAuthorized=$authorized');

Native log writes through the same logEvent(...), so Dart and SDK entries interleave by timestamp in the same daily file.

6. Sending logs to Scope support

Reproduce the issue, tap Export logs, share the ZIP. Open the first .txt first and confirm the header (USER ID, BASE URL, VEHICLE …) is populated β€” empty fields mean the log was captured while logged out, which is a session problem, not a logging one.

7. Troubleshooting

Symptom Likely cause Fix
exportLogs returns null No logs stored yet (fresh install, or logging classes not added to YOUR_FLUTTER_APPLICATION) Confirm LogEventManager/ReportManager are in ios/YOUR_FLUTTER_APPLICATION; reproduce the issue first
Header fields empty Captured while not authorized β€” a session problem See Flutter Session & App Lifecycle Management
Share sheet doesn't appear share_plus not added, or called before a file path returned Add share_plus; guard on non-null path
Only Dart logs, no SDK entries You built a Dart-only logger Use the native store; SDK logs are native (Β§2)
Recent entries missing Native context not flushed on suspend Call saveContext() from the native background handler

Reference

  • iOS Diagnostic Logs & Export β€” the native store and report builder this reuses.
  • Demo files: LogEventManager.swift, ReportManager.swift, LogEvent+CoreData*.
⚠️ **GitHub.com Fallback** ⚠️