State Management
The Fluxy State System - Ultra-scale reactive state management.
State Management
Fluxy v0.1.8 introduces the Fluxy State System - a comprehensive reactive state management engine that provides enterprise-grade features while maintaining 100% backward compatibility.
The New Fluxy State Units
Fluxy now uses its own branded, conflict-free keywords instead of generic "Signals":
| Concept | New Permanent Unit | Legacy Alias (Still Works) |
|---|---|---|
| Core State | Flux<T> | Signal<T> |
| Derived State | FluxComputed<T> | Computed<T> |
| Side Effects | FluxEffect | Effect |
| Async State | AsyncFlux<T> | AsyncSignal<T> |
| Persistence | PersistentFlux<T> | PersistentSignal<T> |
| Collections | FluxList, FluxMap | SignalList, SignalMap |
Basic Usage
// Create a flux
final count = flux(0);
// Create a computed flux
final isDouble = fluxComputed(() => count.value * 2);
// Use it in UI (Still the same ultra-clean Fx)
Fx(() => Fx.text("Count is: ${count.value}"))The "Holy Trinity" of Advanced Features
1. fluxSelector (Targeted Rebuilds)
Solves the "Riverpod Rebuild" problem by allowing you to listen to tiny parts of large objects.
final userFlux = flux(User(name: "John", age: 30));
final userName = fluxSelector(userFlux, (u) => u.name);
// userName widget only rebuilds when name changes, not age!
Fx(() => Fx.text("Name: ${userName.value}"))Benefit: If userFlux.age changes, the userName widget will not rebuild. 100% atomic.
2. fluxWorker (Zero-Jank Computations)
Never freeze your UI again. Automatically moves expensive work to a background Dart Isolate.
// Runs in background isolate
final processedData = fluxWorker(heavyTask, data);
// UI stays responsive
Fx(() => heavyResult.when(
loading: () => Fx.text("Processing..."),
data: (d) => Fx.text("Result: $d"),
error: (e) => Fx.text("Error: $e"),
))Perfect for: Searching large lists, parsing complex JSON, image processing.
3. FluxyLocalMixin (Auto-Cleaning State)
Prevents memory leaks by making state "local" to a widget with automatic cleanup.
class _MyState extends State<MyWidget> with FluxyLocalMixin {
late final localCount = fluxLocal(0); // Auto-disposed!
late final localComputed = fluxLocalComputed(() => localCount.value * 2);
late final localWorker = fluxLocalWorker(_expensiveTask, data);
@override
Widget build(BuildContext context) {
return Fx(() => Fx.button("Count: ${localCount.value}")
.onPressed(() => localCount.value++));
}
}Offline-First Architecture
FluxyRepository for Multi-User Apps
Specialized helpers for multi-user scoping and database stream binding.
class ChatRepository extends FluxyRepository {
// 1. Scoped Persistence (Prevents data leakage between users)
late final messages = flux([],
persistKey: userScope(auth.userId, 'chat_history')
);
// 2. Database Subscription (Auto-cleans up on dispose)
void init() {
bindStream(database.watchMessages(), messages);
}
}State Hydration
Automatic state persistence and restoration across app restarts.
void main() async {
await Fluxy.init(); // Loads ALL saved state from disk!
runApp(MyApp());
}
// Any flux with persistKey is automatically hydrated
final theme = flux("dark", persistKey: "app_theme");
final userPrefs = flux({}, persistKey: "user_preferences");Performance Optimizations
.peek() - Read Without Listening
Read a value without creating a reactive dependency.
// For logging or non-reactive logic
print(count.peek()); // Won't trigger rebuild if count changesuntracked() - Zero Dependency Blocks
Run code blocks without tracking dependencies.
untracked(() {
// Read multiple fluxes without creating circular dependencies
print(count.value);
print(name.value);
});batch() - Group Updates
Prevent intermediate rebuilds when making multiple changes.
batch(() {
count.value++;
name.value = "New Name";
// Subscribing widgets only rebuild once
});Global Middleware System
Intercept every state change across your application for analytics, logging, or state-guarding.
// Create a middleware
class LoggerMiddleware extends FluxyMiddleware {
@override
void onUpdate(String key, dynamic oldValue, dynamic newValue) {
print('[FLUX UPDATE] $key | $oldValue -> $newValue');
}
}
// Register globally
void main() async {
Fluxy.use(LoggerMiddleware());
await Fluxy.init();
runApp(MyApp());
}Time Travel (Undo/Redo)
Built-in history management for any flux.
final textHistory = fluxHistory("Hello");
// Push new state
textHistory.value = "Rocks";
// Undo/Redo
textHistory.undo(); // Back to "Hello"
textHistory.redo(); // Forward to "Rocks"
// Reactive buttons
Fx(() => Fx.button("Undo")
.onPressed(textHistory.canUndo ? textHistory.undo : null)
.opacity(textHistory.canUndo ? 1.0 : 0.5))Async State Management
Enhanced async state with smart error handling and loading states.
final apiResult = fluxAsync(() => fetchData());
// Functional state handling
Fx(() => apiResult.when(
loading: () => Fx.loader(),
data: (data) => Fx.text("Data: $data"),
error: (error) => Fx.text("Error: $error").color(FxTokens.error),
))
// Or using the simplified on() method
Fx(() => apiResult.on(
data: (d) => Fx.text(d),
loading: () => Fx.loader(),
error: (e) => Fx.text("Error: $e"),
))Dependency Injection
Fluxy includes a robust DI system for managing application logic.
Basic Usage
// Register services
Fluxy.put(AuthController());
Fluxy.lazyPut(() => ExpensiveController()); // Lazy loading
// Retrieve services
final auth = Fluxy.find<AuthController>();
final expensive = Fluxy.find<ExpensiveController>();Shorthands
inject(SettingsController());
final settings = use<SettingsController>();Controllers with Lifecycle
class UserController extends FluxyController {
final name = flux("Jane");
@override
void onInit() {
print("User Controller Ready");
}
@override
void onDispose() {
print("Cleaning up resources");
}
}Scoped State
Restrict state to specific widget subtrees.
FluxyProvider(
create: (context) => MyController(),
child: ChildWidget(),
);Advanced Features
Numeric Operators
Automatic computed signals for arithmetic operations.
final a = flux(10);
final b = flux(5);
final sum = a + b; // Automatically a `FluxComputed<num>`Error Handling
Derived fluxes with error recovery.
final result = fluxComputed(
() => throw Exception("Failed"),
onError: (error, stack) => print("Log error: $error"),
);Local State
For state that only lives within a single widget.
class _MyWidgetState extends State<MyWidget> {
final count = flux(0); // Auto-disposed with widget
@override
Widget build(BuildContext context) {
return Fx(() => Fx.button("Count: ${count.value}")
.onPressed(() => count.value++));
}
}Backward Compatibility
All existing Signal code continues to work without modification:
// This still works perfectly
Signal<String> count = flux("hello");
Computed<int> length = computed(() => count.value.length);Legacy aliases are marked with @Deprecated to guide developers toward the new branded API while maintaining compatibility.
Fluxy's state management system now provides enterprise-grade features including background processing, automatic cleanup, offline-first architecture, and global middleware - making it ready for production-scale applications.