Surge
Surge is a lightweight state management library based on Jolt Signals, inspired by the Cubit pattern from the BLoC library. It combines Jolt's reactive signal system with Flutter's state management capabilities, providing a predictable and simple state management solution.
Installation
flutter pub add jolt_surgeCore Concepts
Surge
Surge is a state container class for managing state and reactively notifying changes. It internally uses Jolt's Signal to manage state and provides the emit method to update state.
Basic Usage
import 'package:jolt_surge/jolt_surge.dart';
// Create a Surge
class CounterSurge extends Surge<int> {
CounterSurge() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
}
// Usage
final counter = CounterSurge();
print(counter.state); // 0
counter.increment();
print(counter.state); // 1Creating Surge
Basic Creation
class CounterSurge extends Surge<int> {
CounterSurge() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
}Custom State Creator
You can customize state management through the creator parameter:
class CustomSurge extends Surge<int> {
CustomSurge(int initial) : super(
initial,
creator: (state) => WritableComputed(
() => state,
(value) => state = value,
),
);
}State Access
state
Get the current state value. When accessed in reactive contexts, dependencies are automatically established.
final surge = CounterSurge();
print(surge.state); // 0
Effect(() => print('State: ${surge.state}'));
surge.emit(1); // Effect outputs: "State: 1"raw
Get the underlying reactive value (WritableNode) for advanced scenarios.
final surge = CounterSurge();
final rawValue = surge.raw;
rawValue.value = 43; // Directly set valuestream
Get a stream of state changes.
final surge = CounterSurge();
surge.stream.listen((state) => print('State changed: $state'));
surge.emit(1); // Output: "State changed: 1"
surge.emit(2); // Output: "State changed: 2"State Updates
emit
Emit new state and trigger change notifications.
final surge = CounterSurge();
surge.emit(1); // State changes from 0 to 1
surge.emit(1); // No change (same value)
surge.emit(2); // State changes from 1 to 2Note:
- If the new state is the same as the current state (via
==comparison), no update is triggered - State is updated after
onChangemethod is called - If Surge is already disposed, calling
emitthrows an assertion error
Lifecycle
onChange
Called when state changes. Subclasses can override this method to add custom change handling logic.
class MySurge extends Surge<int> {
MySurge() : super(0);
@override
void onChange(Change<int> change) {
print('State changing from ${change.currentState} to ${change.nextState}');
super.onChange(change);
}
}onDispose
Called when Surge is disposed. Subclasses can override this method to add custom cleanup logic.
class MySurge extends Surge<int> {
MySurge() : super(0);
Timer? _timer;
@override
void onDispose() {
_timer?.cancel();
_timer = null;
super.onDispose();
}
}dispose
Dispose Surge and clean up resources. This method is idempotent—multiple calls have no side effects.
final surge = CounterSurge();
surge.dispose();
// surge.emit(1); // Throws assertion errorChange
The Change class encapsulates state change information, containing the current state and next state.
final change = Change(currentState: 0, nextState: 1);
print('Changing from ${change.currentState} to ${change.nextState}');SurgeObserver
SurgeObserver is an abstract observer class for monitoring Surge lifecycle events.
Creating 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');
}
}Setting 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 calledComplete Example
class CounterSurge extends Surge<int> {
CounterSurge() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
void reset() => emit(0);
}
// Usage
void main() {
final counter = CounterSurge();
// Listen to state changes
counter.stream.listen((state) {
print('Counter: $state');
});
counter.increment(); // Output: "Counter: 1"
counter.increment(); // Output: "Counter: 2"
counter.decrement(); // Output: "Counter: 1"
counter.reset(); // Output: "Counter: 0"
counter.dispose();
}Important Notes
State Immutability: Although Surge allows updating state through
emit, it's recommended to keep state immutable, creating new state on eachemit.Lifecycle Management: When using
SurgeProvider, Surge lifecycle is automatically managed. When creating manually, remember to calldispose().Performance Considerations: Surge internally uses Signal, providing efficient reactive update mechanisms.
Type Safety: Surge provides complete type safety with compile-time type checking.
Test-Friendly: Surge's design makes it easy to test, allowing easy mocking and verification of state changes.