simple_logger_overlay 0.2.2
simple_logger_overlay: ^0.2.2 copied to clipboard
A simple, Dart 3+ compatible Flutter logging plugin with an in-app draggable overlay, log levels, and Dio support
Simple Logger Overlay #
A premium in-app debug logger for Flutter — Material You, zero config, production-safe.
Made with Love & Caffiene by Sam ♥ #
Stop print-debugging.
Stop switching windows.
Everything you need, right inside your app.
The problem this solves #
You're debugging a production-style Flutter app. You need to:
- See logs from a specific user flow — without opening Xcode or Android Studio
- Inspect network request headers and response bodies — directly on device
- Filter only errors from the last 30 seconds — without grep
- Hand your phone to a QA engineer — without them needing a laptop
simple_logger_overlay puts a draggable debug panel inside your app. Tap the FAB, see everything.
Feature highlights #
| Feature | Details | |
|---|---|---|
| 🪄 | Material You UI | Full M3 color tokens · container-transform card transitions · spring-physics FAB |
| ✨ | M3 Expressive motion | Error logs enter with spring overshoot · FAB shadow swells on pulse · animated filter badge dot |
| ⚡ | Live streaming | New logs animate in as they arrive — no pull-to-refresh |
| 🌐 | Network inspector | Dio interceptor · pretty JSON · HTML renderer · per-field copy |
| 🎨 | Themeable | Seed color API · Dynamic Color (Android 12+) · auto dark/light |
| 🔍 | Search & filter | Full-text · M3 FilterChip level/status filter · sort order · M3 modal scrim |
| 🔒 | Non-blocking | Log I/O runs in a background isolate — zero main-thread overhead |
| 🌍 | Localizable | Extend one class to translate every overlay string |
| 📤 | Export | Save logs as .json · copy any field to clipboard |
| 🤝 | Framework agnostic | BLoC · Riverpod · GetX · GoRouter · App lifecycle |
| 🐞 | Shake-to-open | Shake the device to open the overlay (debug builds) |
60-second setup #
1 — Add dependency #
dependencies:
simple_logger_overlay: ^0.2.1
2 — Initialize #
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await SimpleLoggerOverlay.init();
runApp(const MyApp());
}
3 — Add the FAB to your root widget #
final navigatorKey = GlobalKey<NavigatorState>();
MaterialApp(
navigatorKey: navigatorKey,
builder: (context, child) {
return Stack(
children: [
child!,
SimpleOverlayDraggableDebuggerFAB(navigatorKey: navigatorKey),
],
);
},
);
That's it. A draggable button now lives in your app. Drag it anywhere, tap to open the overlay.
Logging #
// Log from anywhere — no context needed
SimpleLoggerOverlay.log('User tapped checkout', level: LogLevel.info, tag: 'CheckoutScreen');
SimpleLoggerOverlay.log('Cart is empty', level: LogLevel.debug, tag: 'CartBloc');
SimpleLoggerOverlay.log('Payment failed: 402', level: LogLevel.error, tag: 'PaymentService');
| Level | Color | Use for |
|---|---|---|
LogLevel.debug |
Teal/tertiary | Detailed internal state |
LogLevel.info |
Primary | Normal flow events |
LogLevel.error |
Error/red | Failures, exceptions |
Network logging #
Add one line to your Dio setup:
import 'package:simple_logger_overlay/core/network_logger_interceptor.dart';
final dio = Dio()
..interceptors.add(SimpleOverlayNetworkLoggerInterceptor());
The network tab shows every request with:
- Method badge + URL
- Status code (green = success, red = error)
- Request headers & body
- Response headers & body — auto-rendered as JSON, HTML, or plain text
- Copy button on every single field
Theming #
🎨 Custom seed color
// Call before runApp — takes effect immediately.
SimpleLoggerOverlayConfig.configure(
seedColor: Colors.deepPurple, // any Color
);
The overlay generates its entire color scheme from a single seed using M3 ColorScheme.fromSeed.
Both light and dark themes are produced automatically.
Default: Color(0xFF52B788) — sage-mint green.
🌈 Android 12+ Dynamic Color (wallpaper-derived)
Add dynamic_color to your app:
dependencies:
dynamic_color: ^1.7.0
DynamicColorBuilder(
builder: (lightDynamic, darkDynamic) {
// Pass the system schemes to the overlay
if (lightDynamic != null) {
SimpleLoggerOverlayConfig.configure(
lightScheme: lightDynamic,
darkScheme: darkDynamic,
);
}
return MaterialApp(/* ... */);
},
);
To revert to seed color:
SimpleLoggerOverlayConfig.configure(
seedColor: Colors.teal,
clearSchemes: true,
);
Integrations #
🧠 BLoC
import 'package:simple_logger_overlay/core/bloc_logger_observer.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
Bloc.observer = SimpleOverlayBlocObserverLogger();
await SimpleLoggerOverlay.init();
runApp(const MyApp());
}
Every Event, Transition, and Error from every BLoC is automatically logged.
🌱 Riverpod
import 'package:simple_logger_overlay/core/riverpod_logger.dart';
runApp(
ProviderScope(
observers: [SimpleOverlayLoggerRiverpodObserver()],
child: const MyApp(),
),
);
⚡ GetX
import 'package:simple_logger_overlay/core/getx_logger_patch.dart';
void main() {
simpleOverlayGetXLogObserver();
runApp(const MyApp());
}
🧭 GoRouter
import 'package:simple_logger_overlay/core/go_router_observer.dart';
final router = GoRouter(
observers: [SimpleOverlayGoRouterObserver()],
routes: [...],
);
Every push, pop, and replace is logged with the full route path.
📱 App Lifecycle
import 'package:simple_logger_overlay/core/app_lifecycle_observer.dart';
WidgetsBinding.instance.addObserver(SimpleOverlayAppLifecycleObserver());
Logs resumed, paused, detached — useful for debugging background/foreground bugs.
Localization #
Add the delegate so the overlay inherits your app's locale:
MaterialApp(
localizationsDelegates: [
SimpleOverlayLocalizations.delegate, // 👈 add this
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
);
Translate overlay strings into any language
Extend SimpleOverlayLocalizations and register your own delegate:
class MySpanishOverlayL10n extends SimpleOverlayLocalizations {
@override String get title => 'Registros';
@override String get searchHint => 'Buscar registros...';
@override String get noLogsTitle => 'Sin registros';
@override String logsTab(int n) => 'Registros ($n)';
// override any of the 25 available getters
}
class MyDelegate extends LocalizationsDelegate<SimpleOverlayLocalizations> {
@override bool isSupported(Locale l) => true;
@override bool shouldReload(_) => true;
@override Future<SimpleOverlayLocalizations> load(Locale locale) {
return Future.value(
locale.languageCode == 'es' ? MySpanishOverlayL10n() : SimpleOverlayLocalizations(),
);
}
}
See the example app for a complete implementation covering English, Spanish, French, Arabic, German, and Japanese.
Console logging #
Logs also print to the terminal with ANSI color and emoji — useful when the device isn't in front of you:
[2025-06-24T19:15:01Z] 🔍 [DEBUG] [CartBloc] Item added: product_id=42
[2025-06-24T19:15:02Z] ℹ️ [INFO] [Checkout] Payment intent created
[2025-06-24T19:15:03Z] 🔥 [ERROR] [PaymentSvc] Stripe returned 402
Disable when not needed:
SimpleOverlayLogStorageService.enableConsole = false;
Open programmatically #
// From any button, gesture, or deep link:
SimpleLoggerOverlay.show(context, navigatorKey: navigatorKey);
How it works #
Your App
│
├─▶ SimpleLoggerOverlay.log(...)
│ │
│ ▼
│ LogStorageService ← singleton, holds broadcast StreamController
│ │
│ ├─▶ Isolate (background)
│ │ └─▶ writes JSONL to disk (non-blocking)
│ │
│ └─▶ stream.add(log)
│ │
│ ▼
│ TabbedLogger (StreamBuilder)
│ └─▶ new card slides in (M3 easing, 350ms)
│
└─▶ SimpleOverlayDraggableDebuggerFAB
└─▶ spring-physics snap to edges
└─▶ pulse animation on new log
└─▶ opens LoggerOverlay (SharedAxisTransition)
└─▶ tap card → OpenContainer transform → LogDetailPage
Screenshots #
| Log list | Network list | Log detail | Network detail | Search & filter | Export |
|---|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
FAQ #
Is this safe to ship in production?
Yes — with a guard. The recommended pattern:
if (kDebugMode) {
SimpleOverlayDraggableDebuggerFAB(navigatorKey: navigatorKey)
}
SimpleLoggerOverlay.log(...) calls are always safe; they just write to a local file. Only the visible FAB and overlay UI need to be gated.
Does it affect app performance?
No. All file I/O is dispatched to a background Isolate. The main thread only receives a StreamController.broadcast event — the same as a state update. The FAB and overlay are rendered in a separate layer from your app's route stack.
How do I clear old logs?
Logs older than N days are purged automatically on next launch. The default is 7 days. To purge manually:
await SimpleOverlayLogStorageService().clearAllLogs();
Can I use it without Dio?
Yes. The Dio interceptor is entirely optional. All other features — simple logs, BLoC/Riverpod/GetX observers, GoRouter observer — work independently.
Does it work on iOS / web / desktop?
- iOS ✅ — all features except Dynamic Color (Android 12+ only)
- macOS / Linux / Windows ✅ — all features (FAB renders in the overlay Stack)
- Web ⚠️ — simple logging works; file export uses a browser download
Dependencies #
| Package | Why |
|---|---|
animations |
Container transform (card→detail) · SharedAxis overlay transition |
google_fonts |
Inter (UI text) · JetBrains Mono (JSON/headers/code) |
flutter_html |
Render HTML response bodies inline |
path_provider |
Persistent JSONL log storage |
share_plus |
Export logs to file |
dio |
Network interceptor |
shake |
Shake-to-open (debug builds) |





