layerx_debugger 1.0.5
layerx_debugger: ^1.0.5 copied to clipboard
Drop-in debugger and logger for Flutter and GetX: pretty console logs, Dio/http capture, crash handling, route, widget and performance tracking, plus an in-app log viewer.
🐛 LayerX Debugger
In-app debugging for Flutter — a new era.
Stop scrolling through console spam. See every log, API call, crash, route and rebuild — live, inside your running app — with automatic blame analysis, API response diffing and a one-line setup.
Why LayerX Debugger? #
Traditional Flutter debugging means squinting at a flood of print statements in a tiny
console, far from the device QA is actually holding. LayerX Debugger flips that — the
debugger lives inside the app. Your testers tap a floating 🐛 button and instantly see a
beautiful, searchable timeline of everything that happened, plus answers to the question that
wastes the most time on every team:
"Who owns this bug — the app, the backend, or the network?"
One call, zero boilerplate, and it never ships to your users (it's environment-aware).
await LayerXDebugger.initialize(); // ✨ that's the whole setup
✨ What you get #
- 🐛 In-app log viewer — a draggable floating button + edge-swipe open a searchable, filterable, color-coded log list with a live session-health banner.
- 🕵️ "Who owns this bug?" blame engine — every error is attributed to app / backend / network with a QA-ready note.
- 🔀 API response diffing — when a backend silently changes its JSON shape, LayerX shows a field-level diff (added / removed / type-changed / value-changed).
- 🎨 Gorgeous console logs —
LayerXLog.d/i/w/e/swith colors, emojis & timestamps, plus boxed┌─ │ └API blocks. - 🌐 Automatic network capture —
LayerXHttp(primary) logs every request/response/error with timings; secrets (password,token,authorization,apiKey,secret) are masked. - 💥 Crash capture —
FlutterError,PlatformDispatcherand zoned errors, with anonCrashhook for Crashlytics / Sentry. - 🧩 GetX-native —
LayerXController/LayerXService/LayerXDebugMixinlifecycle logs, and auto-registered GetX services with duplicate & double-init guards. - ⏱ Profiling & rebuild tracking —
LayerXProfiler.measure(...),LayerXDebugWidget(tag:). - 🌱 Environment-aware —
dev/staging/prodcontrol verbosity, colors and whether the viewer is even available.
📸 See it in action #
Add
doc/viewer.png,doc/detail.pnganddoc/demo.gifto showcase the viewer here.
| Searchable log list | Rich detail + blame | API response diff |
|---|---|---|
doc/viewer.png |
doc/detail.png |
doc/diff.png |
🚀 Install #
dependencies:
layerx_debugger: ^1.0.1
import 'package:layerx_debugger/layerx_debugger.dart';
⚡ Auto-Setup — One Command, Zero Boilerplate #
Added in v1.0.1 — the fastest way to integrate LayerX Debugger into any Flutter project.
After adding the package, just run this once from your Flutter project root:
dart run layerx_debugger:setup
That's it. The CLI will automatically:
| Step | What happens |
|---|---|
| 📦 pubspec | Adds layerx_debugger: ^1.0.1 and runs flutter pub get |
| 🔧 main.dart | Wraps main() with runZonedGuarded + initialize() |
| 🎨 MaterialApp | Injects LayerXDebugOverlay builder + navigatorObservers |
| 💾 Backup | Creates .bak copies before modifying any file |
| 🔁 Idempotent | Safe to run multiple times — already-configured files are skipped |
Before (your original main.dart):
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: const HomeView());
}
}
After (auto-generated by the CLI):
import 'package:layerx_debugger/layerx_debugger.dart';
void main() {
LayerXDebugger.runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
await LayerXDebugger.initialize(
config: const LayerXDebugConfig(appName: 'My App'),
);
runApp(const MyApp());
});
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => LayerXDebugOverlay(child: child!),
navigatorObservers: [LayerXDebugger.routeObserver],
home: const HomeView(),
);
}
}
Run the app — the floating 🐛 button appears automatically. No other changes needed.
You can also target a specific project path:
dart run layerx_debugger:setup /path/to/my_flutter_app
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:layerx_debugger/layerx_debugger.dart';
void main() {
LayerXDebugger.runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
await LayerXDebugger.initialize(
config: const LayerXDebugConfig(appName: 'My App'),
);
runApp(const MyApp());
});
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
navigatorObservers: [LayerXDebugger.routeObserver], // 🧭 route logs
builder: (context, child) => LayerXDebugOverlay(child: child!), // 🐛 in-app viewer
home: const HomeView(),
);
}
}
Run the app in debug mode — a floating 🐛 button appears. Tap it. Done.
🐛 Opening the in-app viewer #
There are four ways to open the viewer — pick whatever fits your app:
// 1. Floating bug button → automatic, from LayerXDebugOverlay (just tap it)
// 2. Edge swipe → swipe in from the right edge (also automatic)
// 3. From ANY button you own:
ElevatedButton(
onPressed: () => LayerXDebugger.openViewer(context),
child: const Text('Open Logs'),
);
// 4. A ready-made settings tile:
const LayerXDebugSettingsButton(),
openViewer(context)works even without the overlay — it simply pushes the viewer screen.
📝 Logging #
LayerXLog.d('User fetched'); // debug (grey)
LayerXLog.i('Cache warmed'); // info (blue)
LayerXLog.s('Payment captured'); // success (green)
LayerXLog.w('Retrying request'); // warning (amber)
LayerXLog.e('API error', error: e, stackTrace: st); // error (red)
LayerXLog.screen('HomeView'); // [SCREEN] HomeView opened
LayerXLog.action('Login Button Clicked'); // [ACTION] Login Button Clicked
'Saved!'.logS(); // or log any value inline
Console output:
┌────────────────────────────────────────────────────────────
│ API GET /users
│ Status : 200 OK • 142ms
│ Response : {"id":1,"name":"Ada"}
└────────────────────────────────────────────────────────────
🌐 Networking — http first #
http is the primary, zero-config integration — just use LayerXHttp:
final res = await LayerXHttp.get(Uri.parse('https://api.example.com/users'));
await LayerXHttp.post(uri, headers: headers, body: jsonBody);
// get / post / put / patch / delete supported.
Dio (optional — never forced) #
LayerX has no dependency on dio. If your app uses Dio, add a tiny interceptor that
forwards to the public LayerXNetworkLogger:
class LayerXDioInterceptor extends Interceptor {
@override
void onResponse(Response res, ResponseInterceptorHandler handler) {
LayerXNetworkLogger.record(
endpoint: res.requestOptions.uri.toString(),
method: res.requestOptions.method,
statusCode: res.statusCode ?? 0,
responseBody: res.data?.toString(),
requestBody: res.requestOptions.data?.toString(),
);
handler.next(res);
}
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
LayerXNetworkLogger.recordException(
endpoint: err.requestOptions.uri.toString(),
method: err.requestOptions.method,
error: err,
stackTrace: err.stackTrace,
);
handler.next(err);
}
}
final dio = Dio()..interceptors.add(LayerXDioInterceptor());
🧩 GetX integration #
class HomeController extends LayerXController {} // lifecycle auto-logged
class AuthService extends LayerXService {}
class CartController extends GetxController with LayerXDebugMixin {} // mix into existing
initialize() auto-registers the LayerX GetX services (LayerXLoggerService,
LayerXDebugService, LayerXCrashService, LayerXNetworkService,
LayerXPerformanceService, LayerXRouteService) as permanent singletons — with
duplicate-prevention and a double-initialization guard. Modules light up incrementally as
controllers and the network layer are exercised. Set isLayerXArchitecture: false (or
autoInject: false) to skip all injection in a non-LayerX app.
🧭 Route debugging #
GetMaterialApp(navigatorObservers: [LayerXDebugger.routeObserver]);
// per-page GetX middleware:
GetPage(name: '/home', page: () => HomeView(), middlewares: [LayerXRouteMiddleware()]);
💥 Crash handling #
await LayerXDebugger.initialize(
config: LayerXDebugConfig(
onCrash: (error, stack, fatal) {
FirebaseCrashlytics.instance.recordError(error, stack, fatal: fatal);
// or: Sentry.captureException(error, stackTrace: stack);
},
),
);
Crashlytics & Sentry are optional — LayerX depends on neither and works standalone.
⏱ Performance & 🔁 rebuilds #
final users = await LayerXProfiler.measure('fetchUsers', () => api.fetchUsers());
LayerXProfiler.start('render'); /* ... */ LayerXProfiler.end('render');
LayerXDebugWidget(tag: 'HomeView', child: HomeView()); // logs "HomeView rebuilt N times"
⚙️ Configuration #
await LayerXDebugger.initialize(
config: LayerXDebugConfig(
appName: 'My App',
environment: LayerXEnvironment.dev, // dev / staging / prod
enableApiLogs: true,
enableRouteLogs: true,
enableCrashLogs: true,
enableGetXLogs: true,
enablePerformanceLogs: true,
enableWidgetLogs: true,
maskKeys: ['ssn', 'cardNumber'],
maxStoredLogs: 500,
edgeSwipeZone: LayerXEdgeZone.right,
autoInject: true,
isLayerXArchitecture: null, // null = auto (assume LayerX); false = skip injection
onCrash: (e, s, fatal) {/* forward */},
),
);
| Environment | Console level | Colors | In-app viewer |
|---|---|---|---|
dev |
verbose+ | ✅ | ✅ |
staging |
info+ | ✅ | ✅ |
prod |
warning+ | ❌ | ❌ |
🏛 Architecture #
LayerX Debugger is itself a showcase of LayerX architecture — the source is organised into
config/, services/, repository/, mvvm/, core/ and widgets/ under lib/src/, while
lib/layerx_debugger.dart stays the single public import.
📖 Example #
A full GetX example lives in example/:
cd example && flutter pub get && flutter run
🤝 Contributing #
Issues and PRs welcome at https://github.com/the-bughex-code/layerx_debugger.
📄 License #
BSD-3-Clause © The BugHex. See LICENSE.