SurgeObserver
SurgeObserver is an abstract observer class for monitoring Surge lifecycle events, including creation, state changes, and disposal. This is very useful for debugging, logging, or implementing cross-cutting concerns.
Basic Usage
Create an observer subclass and override the methods you're interested in, then set it as the global observer:
class MyObserver extends SurgeObserver {
@override
void onCreate(Surge surge) {
print('Surge created: $surge');
}
@override
void onChange(Surge surge, Change change) {
print('State changed: ${change.currentState} -> ${change.nextState}');
}
@override
void onDispose(Surge surge) {
print('Surge disposed: $surge');
}
}
// Set global observer
SurgeObserver.observer = MyObserver();
// Now all Surge lifecycle events will be observed
final surge = CounterSurge();
// onCreate is called
surge.emit(1);
// onChange is called
surge.dispose();
// onDispose is calledLifecycle Methods
onCreate
Called when Surge is created.
class LoggingObserver extends SurgeObserver {
@override
void onCreate(Surge surge) {
print('Created Surge with initial state: ${surge.state}');
}
}onChange
Called when Surge state changes. Called before state is updated, allowing observers to respond to state changes.
class ChangeObserver extends SurgeObserver {
@override
void onChange(Surge surge, Change change) {
print('State change: ${change.currentState} -> ${change.nextState}');
}
}onDispose
Called when Surge is disposed.
class DisposeObserver extends SurgeObserver {
@override
void onDispose(Surge surge) {
print('Surge disposed with final state: ${surge.state}');
}
}Global Observer
SurgeObserver.observer is a static field for setting the global observer. Once set, all Surge instance lifecycle events will notify this observer.
SurgeObserver.observer = MyObserver();
// Now all Surge lifecycle events will be observed
final surge1 = CounterSurge(); // onCreate is called
final surge2 = UserSurge(); // onCreate is called
surge1.emit(1); // onChange is called
surge2.emit(User()); // onChange is called
surge1.dispose(); // onDispose is called
surge2.dispose(); // onDispose is calledPractical Use Cases
Debugging and Logging
class DebugObserver extends SurgeObserver {
@override
void onCreate(Surge surge) {
debugPrint('🔵 Surge created: ${surge.runtimeType}');
}
@override
void onChange(Surge surge, Change change) {
debugPrint('🟢 State changed in ${surge.runtimeType}: ${change.currentState} -> ${change.nextState}');
}
@override
void onDispose(Surge surge) {
debugPrint('🔴 Surge disposed: ${surge.runtimeType}');
}
}
// Enable in development environment
if (kDebugMode) {
SurgeObserver.observer = DebugObserver();
}Performance Monitoring
class PerformanceObserver extends SurgeObserver {
final Map<Surge, Stopwatch> _stopwatches = {};
@override
void onCreate(Surge surge) {
_stopwatches[surge] = Stopwatch()..start();
}
@override
void onChange(Surge surge, Change change) {
final stopwatch = _stopwatches[surge];
if (stopwatch != null) {
print('${surge.runtimeType} state change took ${stopwatch.elapsedMilliseconds}ms');
stopwatch.reset();
stopwatch.start();
}
}
@override
void onDispose(Surge surge) {
final stopwatch = _stopwatches.remove(surge);
if (stopwatch != null) {
print('${surge.runtimeType} lifetime: ${stopwatch.elapsedMilliseconds}ms');
}
}
}State Persistence
class PersistenceObserver extends SurgeObserver {
final Storage _storage;
PersistenceObserver(this._storage);
@override
void onChange(Surge surge, Change change) {
// Save state to persistent storage
_storage.save('${surge.runtimeType}', change.nextState);
}
@override
void onCreate(Surge surge) {
// Try to restore state from persistent storage
final savedState = _storage.load('${surge.runtimeType}');
if (savedState != null) {
// Restore state (needs implementation based on specific Surge type)
}
}
}Analytics Events
class AnalyticsObserver extends SurgeObserver {
final AnalyticsService _analytics;
AnalyticsObserver(this._analytics);
@override
void onCreate(Surge surge) {
_analytics.logEvent('surge_created', {
'type': surge.runtimeType.toString(),
});
}
@override
void onChange(Surge surge, Change change) {
_analytics.logEvent('surge_state_changed', {
'type': surge.runtimeType.toString(),
'from': change.currentState.toString(),
'to': change.nextState.toString(),
});
}
}Important Notes
Performance Impact: Observers are called on every Surge lifecycle event. Ensure observer implementations are efficient and avoid blocking the main thread.
Memory Management: If observers hold references to Surges, it may cause memory leaks. Ensure references are cleaned up when not needed.
Thread Safety: Observer methods may be called in multiple threads. Ensure implementations are thread-safe.
Error Handling: Errors in observers should not affect Surge's normal operation. Consider adding error handling.
Selective Observation: If you only need to observe specific types of Surges, you can add type checks in observer methods.