lifecycle_logger
A debug-focused, zero-UI Flutter utility package for app and widget lifecycle logging.
Features
- App lifecycle observer via
WidgetsBindingObserver - Widget lifecycle logging mixin for
initStateanddispose - Structured
LifecycleEventobjects for app, widget, and route events - Unified event callback via
onEvent - Broadcast stream API via
LifecycleLogger.events - Built-in event filtering (
includeTypes, route/widget filters) - App lifecycle transition callback with previous/current state
- Sink safety with
onSinkErrorerror reporting - Attach-level metadata enrichment for all emitted events
- Pluggable event sink (
sink) for custom telemetry/analytics wiring - Configurable console log tag via
attach(tag: '...') - Optional route lifecycle tracking via
LifecycleLogger.routeObserver - Debug-only by default (
attach(debugOnly: true)) - Minimal API, no dependencies beyond Flutter SDK
Debug-only behavior
By default, LifecycleLogger.attach() runs with debugOnly: true.
- In debug mode: observer registration, logs, and callbacks run normally.
- In profile/release mode:
attach()becomes a no-op.
If you need lifecycle callbacks outside debug (for local testing), pass debugOnly: false.
Usage
import 'package:flutter/widgets.dart';
import 'package:lifecycle_logger/lifecycle_logger.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
LifecycleLogger.attach(
onResume: () => debugPrint('resumed callback'),
onPause: () => debugPrint('paused callback'),
);
runApp(const App());
}
When no longer needed, unregister safely:
LifecycleLogger.detach();
Structured events and custom sink
final events = <LifecycleEvent>[];
LifecycleLogger.attach(
sink: events.add,
onEvent: (event) {
// Runs for every emitted event.
},
logToConsole: false,
tag: '[AppLifecycle]',
debugOnly: false,
);
Stream-based consumption
final subscription = LifecycleLogger.events.listen((event) {
debugPrint('stream event: ${event.type}');
});
// Dispose when no longer needed.
subscription.cancel();
Filtering
LifecycleLogger.attach(
debugOnly: false,
includeTypes: {
LifecycleEventType.appResumed,
LifecycleEventType.appPaused,
},
excludeRouteNames: {'/debug-only'},
onEvent: (event) => debugPrint('filtered: ${event.message}'),
);
Transition callback and sink safety
LifecycleLogger.attach(
debugOnly: false,
onStateTransition: (previous, current, event) {
debugPrint('transition: ${previous?.name ?? 'none'} -> ${current.name}');
},
sink: (event) {
// Your telemetry sink.
},
onSinkError: (error, stackTrace, event) {
debugPrint('sink error for ${event.type}: $error');
},
);
Metadata enrichment
LifecycleLogger.attach(
debugOnly: false,
metadata: const {
'appFlavor': 'staging',
'featureArea': 'navigation',
},
onEvent: (event) {
debugPrint('metadata: ${event.metadata}');
},
);
Route lifecycle tracking
Attach the provided observer to your app navigator.
LifecycleLogger.attach(
enableRouteObserver: true,
debugOnly: false,
);
MaterialApp(
navigatorObservers: [LifecycleLogger.routeObserver],
home: const HomePage(),
);
Widget lifecycle mixin
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> with LifecycleAware<MyWidget> {
@override
void onInit() {
// Custom init hook
}
@override
void onDispose() {
// Custom dispose hook
}
@override
Widget build(BuildContext context) {
return const SizedBox.shrink();
}
}
Log format
[Lifecycle] App resumed[Lifecycle] App paused[Lifecycle] MyWidget initState[Lifecycle] MyWidget dispose
Author
Built by Jashwanth Neela.