Flutter Diagnostic Logs & Export - scopetech/Mobile-Public GitHub Wiki
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.
- 1. Principle
- 2. Native side, the log store and export
- 3. Channel methods
- 4. Dart side, trigger and share
- 5. Including your Dart logs, optional
- 6. Sending logs to Scope support
- 7. Troubleshooting
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.
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'
endExtend 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)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).
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.
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.
| 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 |
- iOS Diagnostic Logs & Export β the native store and report builder this reuses.
- Demo files:
LogEventManager.swift,ReportManager.swift,LogEvent+CoreData*.