joker_state 4.1.0
joker_state: ^4.1.0 copied to clipboard
Joker is a lightweight, reactive state management solution for Flutter that brings the fun of the circus to your codebase with simple, flexible, and type-safe state handling.
🃏 JokerState #
⚠️ Breaking Changes in v4.0.0:
CircusRing
is now a standalone package. While still usable in JokerState, it no longer provides Joker-specific integration. Please use circus_ring.RingCueMaster
now leveragesrx_dart
for a more robust Event Bus system.JokerStage
andJokerFrame
constructors are now private. Please use theperform
andfocusOn
APIs instead.- Both
Joker
andPresenter
are now based onRxInterface
, providing more flexible and efficient state management. RxInterface
is built onBehaviorSubject
and internally usesTimer
for improved autoDispose handling.JokerPortal
andJokerCast
are deprecated. For context-free state management, use CircusRing API withPresenter
.JokerReveal
is deprecated. Use Dart's native language features for conditional rendering.JokerTrap
is deprecated. UsePresenter
'sonDone
orStatefulWidget
'sdispose
for controller management.
JokerState is a lightweight, reactive Flutter state management toolkit based on rx_dart
, with integrated dependency injection via circus_ring.
With just the Joker
, Presenter
, and CircusRing
APIs, you can flexibly manage state and dramatically reduce boilerplate.
Features #
- 🧠 Reactive State Management: Automatic widget rebuilds and side-effect execution.
- 💉 Dependency Injection: Simple DI with the CircusRing API.
- 🪄 Selective Rebuilds: Fine-grained control over what triggers UI updates.
- 🔄 Batch Updates: Combine multiple state changes into a single notification.
- 🏗️ Record Support: Combine multiple states using Dart Records.
- 🧩 Modular Design: Import only what you need, or use the full package.
- 📢 Event Bus: Type-safe event system via RingCueMaster.
- ⏱️ Timing Controls: Debounce, throttle, and more for smooth UX.
Quick Start #
Add JokerState to your pubspec.yaml
:
dependencies:
joker_state: ^latest_version
Then import the package:
import 'package:joker_state/joker_state.dart';
Core Concepts #
🎭 Joker: Local Reactive State Container #
Joker<T>
is based on RxInterface
and provides a local reactive state container. Its lifecycle is mainly managed by listeners and the keepAlive
parameter, while providing a whisper
API for manual control and a batch
API for batch updates.
// Create a Joker (auto-notifies by default)
final counter = Joker<int>(0);
// Update state and notify all listeners
counter.trick(1);
// Update using a function
counter.trickWith((current) => current + 1);
// Or simply
counter.state = 1;
✨ Presenter #
Presenter<T>
is an advanced version of Joker. Based on the additional onInit
, onReady
, and onDone
lifecycle hooks, it provides developers with more sophisticated operations and can easily implement BLoC, MVC, and MVVM patterns.
class MyCounterPresenter extends Presenter<int> {
MyCounterPresenter() : super(0);
@override
void onInit() { /* Initialization */ }
@override
void onReady() { /* Safe to interact with WidgetsBinding */ }
@override
void onDone() { /* Clean up resources */ }
void increment() => trickWith((s) => s + 1);
}
// Usage:
final myPresenter = MyCounterPresenter();
myPresenter.increment();
// dispose() automatically calls onDone()
myPresenter.dispose();
🎪 CircusRing: Dependency Injection #
CircusRing is a lightweight dependency container, now a standalone package (circus_ring), but still usable within JokerState.
🎭 Simple Reactive UI Integration #
JokerState provides various widgets for seamless state and UI integration:
The Simplest Usage
// Using Joker
final userJoker = Joker<User>(...);
userJoker.perform(
builder: (context, user) => Text('Name: ${user.name}'),
)
// Using Presenter
final myPresenter = MyPresenter(...);
myPresenter.perform(
builder: (context, state) => Text('State: $state'),
)
For more details, see State Management.
📢 RingCueMaster: Event Bus System #
Type-safe event bus for communication between components:
// Define event type
class UserLoggedIn extends Cue {
final User user;
UserLoggedIn(this.user);
}
// Access the global event bus
final cueMaster = Circus.ringMaster();
// Listen for events
final subscription = Circus.onCue<UserLoggedIn>((event) {
print('User ${event.user.name} logged in at ${event.timestamp}');
});
// Send event
Circus.sendCue(UserLoggedIn(currentUser));
For more details, see Event Bus.
⏱️ CueGate: Timing Controls #
Manage actions with debounce and throttle:
// Create a debounce gate
final debouncer = CueGate.debounce(delay: Duration(milliseconds: 300));
// Use in event handlers
TextField(
onChanged: (value) {
debouncer.trigger(() => performSearch(value));
},
),
// Create a throttle gate
final throttler = CueGate.throttle(interval: Duration(seconds: 1));
// Limit UI updates
scrollController.addListener(() {
throttler.trigger(() => updatePositionIndicator());
});
// In StatefulWidget, use the mixin for automatic cleanup
class SearchView extends StatefulWidget {
// ...
}
class _SearchViewState extends State<SearchView> with CueGateMixin {
void _handleSearchInput(String query) {
debounceTrigger(
() => _performSearch(query),
Duration(milliseconds: 300),
);
}
void _handleScroll() {
throttleTrigger(
() => _updateScrollPosition(),
Duration(milliseconds: 100),
);
}
// Cleanup handled automatically by mixin
}
For more details, see Timing Controls.
Advanced Features #
🔄 Side-Effects #
Listen for state changes and execute side-effects:
final counter = Joker<int>(0);
counter.effect(
child: Container(),
effect: (context, state) {
print('State changed: $state');
},
runOnInit: true,
effectWhen: (prev, val) => (prev!.value ~/ 5) != (val.value ~/ 5),
);
Additional Info #
JokerState is designed to be lightweight, flexible, and powerful—offering reactive state management and dependency injection in one cohesive package.
When should you use JokerState? #
- You want something simpler than BLoC or other complex state solutions
- You need reactive UI updates with minimal boilerplate
- You want the flexibility to control things manually when needed
- You need integrated dependency management
- You prefer clear, direct state operations (not abstract concepts)
- You need a type-safe event bus for decoupled communication
- You want utility widgets that work well with your state management
License #
MIT