Fluxy
Fundamentals

State Management

The Fluxy State System - Ultra-scale reactive state management.

State Management

Fluxy v1.0.0 provides the Fluxy State System - a comprehensive reactive state management engine that provides enterprise-grade features with modular architecture support.

The Fluxy State Units

Fluxy uses its own branded reactive state management system:

ConceptFluxy UnitDescription
Core StateFlux<T>Reactive signal for primitive and complex data
Derived StateFluxComputed<T>Computed values that automatically update
Side EffectsFluxEffectReactive side effects and async operations
Async StateAsyncFlux<T>Built-in async state handling
CollectionsFluxList, FluxMapReactive collections
Persistenceflux<T>(..., persist: true)Built-in state persistence

Basic Usage (v1.0.0)

import 'package:fluxy/fluxy.dart';

// Create a reactive signal
final count = flux(0); 

// Create a computed signal
final isDouble = fluxComputed(() => count.value * 2);

// Use it in UI (atomic reactivity)
Fx(() => Fx.text("Count is: ${count.value}"))

Note: The flux function creates Flux<T> objects that automatically update UI when values change. This is the core of Fluxy's reactivity system.

Advanced Features

Persistence

Fluxy v1.0.0 includes built-in state persistence with encryption:

// Persistent state (automatically saved/loaded)
final session = flux<User?>(null, key: 'auth_session', persist: true);

// Usage in UI
Fx(() => Fx.text("Welcome, ${session.value?.name ?? 'Guest'}"))

Selectors

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.

3. asyncFlux (Server State)

Simplify API calls and async operations with built-in loading/error states:

// Automatic async state management
final users = asyncFlux(() => Fx.http.get('/api/users'));

// UI automatically handles loading/error/data states
Fx(() => users.on(
  data: (list) => UserList(users: list),
  loading: () => Fx.loader.shimmer(),
  error: (e) => ErrorView(e),
))

4. fluxLocal (Component State)

Scoped state that automatically disposes with the widget using FluxyLocalMixin:

class MyWidget extends StatefulWidget {
  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> with FluxyLocalMixin {
  @override
  Widget build(BuildContext context) {
    // Local state - automatically disposed when widget is removed
    final localCount = fluxLocal(0);
    
    return Fx.button("Count: ${localCount.value}")
      .onTap(() => localCount.value++);
  }
}

5. fluxEffect (Side Effects)

Handle side effects when state changes:

final count = flux(0);

fluxEffect(() {
  print('Count changed: ${count.value}');
  // Perform side effects here
}, [count]);

Modular Architecture Integration

With v1.0.0, state management works seamlessly with modular packages:

import 'package:fluxy/fluxy.dart';
import 'package:fluxy_auth/fluxy_auth.dart';
import 'package:fluxy_storage/fluxy_storage.dart';

class AppController extends FluxController {
  // Core state management
  final isLoading = flux(false);
  final error = flux<String?>(null);
  
  // Modular state integration
  final authState = fluxSelector(Fx.platform.auth.state, (s) => s);
  final userPrefs = flux<Map<String, dynamic>>({});
  
  @override
  void onInit() {
    super.onInit();
    _loadUserData();
  }
  
  Future<void> _loadUserData() async {
    isLoading.value = true;
    try {
      // Use modular storage
      final prefs = await Fx.platform.storage.getAll();
      userPrefs.value = prefs;
    } catch (e) {
      error.value = e.toString();
    } finally {
      isLoading.value = false;
    }
  }
}

Professional Logging (v0.2.6+)

Fluxy includes professional logging for state operations:

// Logs will show:
// [DATA] [AUDIT] Flux created: count
// [DATA] [AUDIT] Flux updated: count = 5
// [DATA] [REPAIR] Auto-repair applied to circular dependency

final count = flux(0);
count.value = 5; // [DATA] [AUDIT] Flux updated: count = 5

Performance Features

1. Batch Updates

Fluxy automatically batches multiple state updates:

// These updates are batched into a single rebuild
count.value = 1;
name.value = "John";
isLoading.value = false;

2. Dependency Tracking

Only rebuild widgets that actually use the changed state:

// Only rebuilds when count changes
Fx(() => Fx.text("Count: $count"))

// Only rebuilds when name changes  
Fx(() => Fx.text("Name: $name"))

3. Memory Management

Automatic cleanup and weak references prevent memory leaks:

// Automatically disposed when no longer referenced
final tempState = flux(temporaryData);

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.

2.5 fluxIsolate (Task Isolation)

A high-level wrapper for executing one-off heavy tasks in a separate isolate without setting up a continuous worker.

final result = await fluxIsolate(() {
  // Expensive synchronous computation
  return crunchNumbers(data);
});

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, scope-aware DI system for managing application logic and service lifecycles.

Scoped Lifecycles (FxScope)

v0.2.0 introduces structured scopes to manage memory automatically and prevent leaks.

ScopeLifecycleBest For
FxScope.appLives for the entire app duration.Auth, Theme, Settings.
FxScope.routeDisposed when the parent route is popped.Feature Controllers, Page Logic.
FxScope.factoryCreates a new instance on every request.Transient utilities, DTOs.

Basic Usage

// Register services with specific scopes
FluxyDI.put(AuthController(), scope: FxScope.app);
FluxyDI.put(ProfileController(), scope: FxScope.route);

// Lazy loading (instance created only when first used)
FluxyDI.lazyPut(() => ExpensiveController()); 

Retrieving Dependencies

// Standard retrieval
final auth = FluxyDI.find<AuthController>();

// Using shorthands
final profile = use<ProfileController>();

Automatic Route Cleanup

When using FxScope.route, Fluxy automatically identifies the active route context. When that route is removed from the navigation stack, the dependency is disposed and its onClose() method is called.

Circular Dependency Detection

The v0.2.0 DI engine includes a proactive tracker that blocks infinite resolution loops. If ServiceA requires ServiceB and vice-versa, Fluxy will throw a descriptive FluxyDIException during initialization instead of hanging the application.

Controllers with Lifecycle

FluxController provides native lifecycle hooks that are automatically managed by the Fluxy engine. This is where you should place your business logic.

class UserController extends FluxController {
  final user = flux<User?>(null);

  @override
  void onInit() {
    // Called when the controller is first initialized.
    // Perfect for setting up listeners or initial data fetching.
    super.onInit();
    print("User Controller Initialized");
  }

  @override
  void onReady() {
    // Called after the first frame is rendered.
    // Safe to show snackbars, dialogs, or perform navigation.
    super.onReady();
  }

  @override
  void onClose() {
    // Called when the controller is removed from memory.
    // Use for cleaning up streams, timers, or manual resources.
    super.onClose();
  }
}

Architectural Scaffolding

Fluxy v0.1.9 introduces Blueprints and the FluxRepository pattern to standardize enterprise application development.

FluxRepository

Standardized data layer that handles offline-first orchestration.

class AuthRepository extends FluxRepository {
  late final session = flux(null, persistKey: 'user_session');

  Future<void> login(String email, String password) async {
    final data = await Fx.http.post('/auth/login', body: {'email': email, 'password': password});
    session.value = data;
  }
}

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.


The Master State Implementation

In a professional application, you don't just use single signals. You build a Unified Reactive Store. Here is how you tie everything together in a real-world project.

class UserProfileStore extends FluxController {
  // 1. Core Reactive Data (With Labels for Snapshots)
  final profile = flux<User>(User.guest(), label: 'user_profile');
  final theme = flux('dark', label: 'app_theme', persist: true);
  
  // 2. Computed Logic (Derived from multiple signals)
  late final displayName = fluxComputed(() {
    return profile.value.isGuest ? "Welcome Guest" : "Member: ${profile.value.name}";
  });

  // 3. Selection (Subscribe only to specific properties)
  late final avatarUrl = fluxSelector(profile, (p) => p.photoUrl);

  // 4. Async Data Loading
  late final activityStats = asyncFlux(() => Fx.http.get('/api/stats'));

  // 5. History (Undo / Redo for Settings)
  final bio = fluxHistory("Write something...", label: 'user_bio');

  @override
  void onInit() {
    super.onInit();
    // 6. Side Effects
    fluxEffect(() {
      print("System Alert: Theme changed to ${theme.value}");
      Fx.toast.info("Applying ${theme.value} mode");
    });
  }
}

// 7. Standardized UI Usage
class ProfileView extends StatelessWidget {
  final store = use<UserProfileStore>();

  @override
  Widget build(BuildContext context) {
    return Fx.col(
      gap: 16,
      children: [
        // Atomic UI - only this text rebuilds on name change
        Fx(() => Fx.text(store.displayName.value).font.bold()),
        
        // Complex Async State
        Fx(() => store.activityStats.on(
          loading: () => Fx.shimmer(child: Fx.box().h(100)),
          data: (stats) => Fx.text("Recent Activity: $stats"),
          error: (e) => Fx.text("Stats unavailable").color(Colors.red),
        )),

        // Persistence in Action
        Fx.button("Toggle Theme", onTap: () {
          store.theme.update((t) => t == 'dark' ? 'light' : 'dark');
        }),
      ],
    );
  }
}

Fluxy's state management system provides enterprise-grade features including background processing, automatic cleanup, and global middleware - making it ready for production-scale applications.

On this page