Fluxy

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":

ConceptNew Permanent UnitLegacy Alias (Still Works)
Core StateFlux<T>Signal<T>
Derived StateFluxComputed<T>Computed<T>
Side EffectsFluxEffectEffect
Async StateAsyncFlux<T>AsyncSignal<T>
PersistencePersistentFlux<T>PersistentSignal<T>
CollectionsFluxList, FluxMapSignalList, 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 changes

untracked() - 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.

On this page