Table of Contents
1. Why Flutter Needs Remote Logging in Production
Flutter's debugPrint() and print() statements only output to the attached debugger console. Once your app ships to users, those logs disappear. You're flying blind.
Consider what happens when a user in Tokyo reports a crash on their Samsung Galaxy S24 running Android 14. You can't attach a debugger. You can't reproduce it locally. Without remote logging and crash reporting, you're guessing.
Without Remote Logging
- ✗ Crash data lost forever
- ✗ No context about what user did before crash
- ✗ Obfuscated stack traces unreadable
- ✗ Hours debugging device-specific bugs
- ✗ No visibility into production performance
With Logtrics + Flutter
- ✓ Real-time crash notifications
- ✓ Full session logs before crash
- ✓ Auto-symbolicated Dart stack traces
- ✓ Device, OS, and Flutter version context
- ✓ AI root cause analysis in seconds
2. Flutter Logging Approaches: Local vs. Remote
There are two fundamental approaches to Flutter logging. Most developers start with local logging during development — but production apps need remote logging.
| Approach | Use Case | Production Ready? | Examples |
|---|---|---|---|
| Local logging | Development & debugging | No | print(), logger package |
| Remote logging | Production monitoring | Yes | Logtrics, custom backend |
| Crash reporting | Unhandled error capture | Yes | Logtrics, Sentry, Crashlytics |
For production Flutter apps, you need both: a remote logging SDK to stream logs from user devices, and a crash reporter to capture and symbolicate unhandled exceptions. Logtrics provides both in a single SDK.
3. Setting Up Flutter Crash Reporting
Flutter has two separate crash reporting surfaces you need to handle:
- Dart/Flutter exceptions — caught via
FlutterError.onError - Platform/native crashes — caught via
PlatformDispatcher.instance.onError - Isolate errors — caught via
Isolate.current.addErrorListener()
Here's the complete initialization setup:
import 'package:flutter/material.dart';
import 'package:logtrics/logtrics.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Logtrics before runApp
await Logtrics.initialize(
apiKey: 'your-api-key',
appVersion: '1.0.0',
environment: 'production',
);
// Capture Flutter framework errors
FlutterError.onError = (details) {
Logtrics.reportCrash(details.exception, details.stack);
};
// Capture async errors outside Flutter framework
PlatformDispatcher.instance.onError = (error, stack) {
Logtrics.reportCrash(error, stack);
return true;
};
runApp(const MyApp());
}
Tip: Always initialize Logtrics before runApp() to ensure it can capture crashes that occur during app initialization.
4. Symbolication: Reading Dart Stack Traces
When you build a Flutter app with --obfuscate and --split-debug-info (which you should for release builds), Dart stack traces become unreadable:
// Obfuscated crash (unreadable):
at Object.a (file:///data/app/com.example.app-1/base.apk:1:294850)
at Object.b (file:///data/app/com.example.app-1/base.apk:1:294903)
Logtrics automatically symbolicates these traces using the .symbols files you upload at build time, converting them back into human-readable stack frames with file names and line numbers.
# Build with debug symbols
flutter build appbundle \
--obfuscate \
--split-debug-info=symbols/
# Upload symbols to Logtrics
logtrics upload-symbols \
--api-key your-api-key \
--symbols-dir symbols/ \
--app-version 1.0.0
5. Remote Log Streaming with Logtrics
Once Logtrics is initialized, use it as your primary logging interface throughout your Flutter app. Logs are batched and transmitted efficiently to minimize battery and bandwidth impact.
// Structured logging with levels
Logtrics.debug('User opened checkout flow');
Logtrics.info('Payment initiated', metadata: {
'amount': 29.99,
'currency': 'USD',
'method': 'apple_pay',
});
Logtrics.warn('Slow network detected', metadata: {
'latency_ms': 2400,
});
Logtrics.error('Payment failed', error: e, stackTrace: st);
// Identify users for session correlation
Logtrics.setUser(
id: user.id,
email: user.email,
name: user.displayName,
);
// Track custom events for behavioral analytics
Logtrics.trackEvent('purchase_completed', properties: {
'product_id': 'pro_monthly',
'revenue': 29.99,
});
All logs are streamed to the Logtrics dashboard in real-time. You can filter by user ID, session, log level, or any custom metadata to pinpoint exactly what happened before a crash.
6. AI-Powered Root Cause Analysis
Logtrics doesn't just collect your crashes — it analyzes them. When a Flutter crash comes in, the AI engine:
Analyzes Stack Trace
Reads the symbolicated Dart stack trace and identifies the root call chain
Correlates Session Logs
Joins the crash with the full log history from that session
Generates Fix Suggestions
Outputs an actionable root cause summary with suggested code fixes
Instead of spending hours reading stack traces and log files, you get a 3-paragraph AI summary explaining exactly what went wrong, why, and what to change. For Flutter's notoriously verbose async error chains, this is a major productivity multiplier.
7. Flutter Logging Best Practices
Log at the right level
Use debug for development noise, info for important business events, warn for degraded states, and error only for failures. In production, set the minimum level to info to reduce noise and data costs.
Always include structured metadata
Don't log strings like "Payment failed for user 123". Instead, log the message as-is and pass user ID, error code, and amount as structured metadata. This makes filtering and grouping in the dashboard far more powerful.
Set user context early
Call Logtrics.setUser() as soon as you know who the user is (after auth). This ties all logs and crashes to a specific user, making it easy to reproduce issues by replaying their exact session.
Never log PII or secrets
Strip passwords, auth tokens, full credit card numbers, and SSNs before logging. Log masked/hashed versions instead (e.g., ****1234 for card numbers). Logtrics supports custom log scrubbing rules at the SDK level.
Use release mode flag for verbosity control
Use kReleaseMode to automatically adjust log verbosity. In debug, log everything. In release, only log info and above to keep production noise low and transmission efficient.
8. Frequently Asked Questions
How do I add crash reporting to a Flutter app?
Add the Logtrics Flutter SDK to your pubspec.yaml, initialize it in main() with your API key, and wire up FlutterError.onError and PlatformDispatcher.instance.onError. Logtrics automatically captures both Dart and native (iOS/Android) crashes in production.
What is the best logging package for Flutter?
For production apps, a remote logging solution like Logtrics is essential. It streams logs in real-time from devices, lets you filter by user or session, and provides AI-powered root cause analysis — unlike local packages like logger that only work during development.
How do I debug Flutter crashes in production?
Use a crash reporting tool that supports Flutter symbolication. Logtrics automatically symbolicates Dart stack traces using your uploaded .symbols files, correlates crash data with device info and session logs, and uses AI to suggest root causes and fixes.
Does Flutter have built-in crash reporting?
No. Flutter only logs to the attached debugger console during development. For production monitoring you need a third-party SDK. Firebase Crashlytics is free but mobile-only and lacks remote logging. Logtrics adds remote logging, AI analysis, and session replay in a single SDK.
Ready to Debug Flutter Apps 10x Faster?
Join 500+ mobile developers using Logtrics to catch, diagnose, and fix Flutter crashes in production — before users notice.