Migration Guide v0.1.8
How to migrate to the new Fluxy State System in v0.1.8.
Migration Guide v0.1.8
Fluxy v0.1.8 introduces the Fluxy State System - a major upgrade that gives Fluxy a unique identity while maintaining 100% backward compatibility.
What Changed?
New Branded Keywords
Fluxy now uses its own branded, conflict-free keywords instead of generic "Signals":
| Old Keyword | New Fluxy Keyword | Status |
|---|---|---|
Signal<T> | Flux<T> | Recommended |
Computed<T> | FluxComputed<T> | Recommended |
Effect | FluxEffect | Recommended |
AsyncSignal<T> | AsyncFlux<T> | Recommended |
PersistentSignal<T> | PersistentFlux<T> | Recommended |
SignalList, SignalMap | FluxList, FluxMap | Recommended |
New Advanced Features
fluxSelector- Targeted rebuilds to prevent unnecessary UI updatesfluxWorker- Background isolate processing for heavy computationsFluxyLocalMixin- Automatic state cleanup to prevent memory leaksFluxyRepository- Offline-first architecture with multi-user support- Global Middleware - Intercept all state changes for analytics/logging
- State Hydration - Automatic state persistence across app restarts
Backward Compatibility
Your existing code will continue to work without any changes!
// This still works perfectly in v0.1.8
Signal<String> count = flux("hello");
Computed<int> length = computed(() => count.value.length);
Effect(() => print(count.value), [count]);Legacy aliases are marked with @Deprecated but will continue to function. You'll see deprecation warnings to guide you toward the new API.
Recommended Migration Path
Step 1: Update Core Types
Replace the basic types with their Fluxy equivalents:
// Before
Signal<int> count = flux(0);
Computed<String> displayName = computed(() => "Count: ${count.value}");
// After
Flux<int> count = flux(0);
FluxComputed<String> displayName = fluxComputed(() => "Count: ${count.value}");Step 2: Update Function Names
Use the new flux... prefixed functions:
// Before
final computed = computed(() => value * 2);
final effect = Effect(() => print(value), [value]);
// After
final computed = fluxComputed(() => value * 2);
final effect = FluxEffect(() => print(value), [value]);Step 3: Add Performance Optimizations
Take advantage of the new performance features:
// Replace manual optimization with fluxSelector
// Before
final userName = fluxComputed(() => user.value.name);
// After - only rebuilds when name changes, not entire user object
final userName = fluxSelector(user, (u) => u.name);Step 4: Add Automatic Cleanup
Use FluxyLocalMixin for widget-local state:
// Before
class _MyWidgetState extends State<MyWidget> {
late final count = flux(0);
@override
void dispose() {
count.dispose(); // Manual cleanup
super.dispose();
}
}
// After - automatic cleanup
class _MyState extends State<MyWidget> with FluxyLocalMixin {
late final count = fluxLocal(0); // Auto-disposed!
}Step 5: Enable State Hydration
Add automatic state persistence:
// Before
void main() {
runApp(MyApp());
}
// After
void main() async {
await Fluxy.init(); // Load all saved state
runApp(MyApp());
}
// Any flux with persistKey is now automatically hydrated
final theme = flux("dark", persistKey: "app_theme");Code Examples
Basic Counter Migration
// Before (v0.1.7)
class CounterController {
final count = Signal(0);
final isEven = Computed(() => count.value % 2 == 0);
void increment() => count.value++;
}
// After (v0.1.8)
class CounterController {
final count = flux(0);
final isEven = fluxComputed(() => count.value % 2 == 0);
void increment() => count.value++;
}Advanced State Management
// Before
class UserController extends FluxyController {
final user = Signal<User?>(null);
final isLoading = Signal(false);
Future<void> loadUser(String id) async {
isLoading.value = true;
try {
user.value = await api.getUser(id);
} finally {
isLoading.value = false;
}
}
}
// After - with new features
class UserController extends FluxyController {
final user = flux<User?>(null);
final isLoading = flux(false);
// Use fluxWorker for heavy operations
late final userLoader = fluxWorker(_loadUser, '');
// Use fluxSelector for derived state
late final userName = fluxSelector(user, (u) => u?.name ?? 'Guest');
Future<void> loadUser(String id) async {
isLoading.value = true;
userLoader.value = id; // Runs in background isolate
}
Future<User> _loadUser(String id) async {
return await api.getUser(id);
}
}Repository Pattern
// Before
class ChatRepository {
final messages = Signal<List<Message>>([]);
void init() {
// Manual stream handling
database.watchMessages().listen((data) {
messages.value = data;
});
}
}
// After - with FluxyRepository
class ChatRepository extends FluxyRepository {
// Scoped persistence prevents data leakage between users
late final messages = flux([],
persistKey: userScope(auth.userId, 'chat_history')
);
void init() {
// Automatic cleanup on dispose
bindStream(database.watchMessages(), messages);
}
}New Features You Should Use
1. Targeted Rebuilds
// Instead of recomputing everything when user changes
final user = flux(User(name: "John", age: 30, email: "john@example.com"));
// Use fluxSelector for specific properties
final userName = fluxSelector(user, (u) => u.name); // Only rebuilds for name changes
final userAge = fluxSelector(user, (u) => u.age); // Only rebuilds for age changes2. Background Processing
// Heavy computations no longer block UI
final searchResults = fluxWorker(searchDatabase, "");
// UI stays responsive
Fx(() => searchResults.when(
loading: () => Fx.loader(),
data: (results) => Fx.list(results),
error: (e) => Fx.text("Search failed: $e"),
))3. Global Middleware
// Add analytics to every state change
class AnalyticsMiddleware extends FluxyMiddleware {
@override
void onUpdate(String key, dynamic oldValue, dynamic newValue) {
Analytics.trackStateChange(key, newValue);
}
}
void main() async {
Fluxy.use(AnalyticsMiddleware());
await Fluxy.init();
runApp(MyApp());
}Breaking Changes
There are no breaking changes in v0.1.8. All existing code continues to work.
However, these deprecation warnings will appear:
Signalis deprecated, useFluxComputedis deprecated, useFluxComputedEffectis deprecated, useFluxEffectAsyncSignalis deprecated, useAsyncFlux
Benefits of Migrating
- Performance:
fluxSelectorprevents unnecessary rebuilds - Responsiveness:
fluxWorkerkeeps UI smooth during heavy operations - Memory Safety:
FluxyLocalMixinprevents memory leaks - Offline-First:
FluxyRepositoryenables robust offline apps - Analytics: Global middleware provides insights
- Persistence: Automatic state hydration
- Branding: Unique identity that won't conflict with other packages
Migration Checklist
- Replace
Signal<T>withFlux<T> - Replace
Computed<T>withFluxComputed<T> - Replace
EffectwithFluxEffect - Replace
AsyncSignal<T>withAsyncFlux<T> - Add
await Fluxy.init()to main() - Use
FluxyLocalMixinfor widget-local state - Add
fluxSelectorfor derived state - Use
fluxWorkerfor heavy computations - Consider
FluxyRepositoryfor offline features - Add middleware for analytics if needed
Ready for production: The new Fluxy State System provides enterprise-grade features while maintaining the simple, clean API you love.