FlutterEffect
FlutterEffect is a side effect implementation specifically designed for Flutter, scheduled to execute at the end of the current Flutter frame. This merges multiple triggers within the same frame into a single execution, avoiding unnecessary repeated executions during frame rendering, which is very useful for UI-related side effects that should not interfere with frame rendering.
Basic Usage
import 'package:jolt_flutter/jolt_flutter.dart';
final count = Signal(0);
// Effect executes at the end of the current frame, even if count changes multiple times in the same frame
final effect = FlutterEffect(() {
print('Count is: ${count.value}');
});
count.value = 1;
count.value = 2;
count.value = 3;
// Effect executes once at the end of the current frame, outputs: "Count is: 3"Creating FlutterEffect
Immediate Execution (Default)
By default, FlutterEffect executes immediately upon creation and immediately collects dependencies, then executes at the end of the current frame when dependencies change:
final signal = Signal(0);
// Effect executes immediately and collects dependencies
final effect = FlutterEffect(() {
print('Signal value: ${signal.value}');
}); // Immediately outputs: "Signal value: 0"
signal.value = 1; // Effect executes at the end of the current frame
signal.value = 2; // Effect executes at the end of the current frame (merged into one)Lazy Dependency Collection
Using lazy: true delays dependency collection. FlutterEffect does not execute immediately and does not collect dependencies—you must manually call run() to start collecting dependencies. This is suitable for "define first, use later" scenarios:
final signal = Signal(0);
// Define FlutterEffect first, but don't collect dependencies
final effect = FlutterEffect(() {
print('Signal value: ${signal.value}');
}, lazy: true); // Does not execute immediately, does not collect dependencies
// Manually start collecting dependencies later
effect.run(); // Now starts collecting dependencies and executes, outputs: "Signal value: 0"
signal.value = 1; // Effect executes at the end of the current frameYou can also use the FlutterEffect.lazy factory method:
final effect = FlutterEffect.lazy(() {
print('Signal value: ${signal.value}');
});Use Cases: Lazy dependency collection is mainly used when you need to define a FlutterEffect first and activate it later, such as defining it during component initialization and activating it at a specific time.
Manual Execution
You can use the run() method to manually trigger FlutterEffect execution. For FlutterEffects with lazy: true, run() starts collecting dependencies and executes:
final signal = Signal(0);
final effect = FlutterEffect(() {
print('Signal value: ${signal.value}');
}, lazy: true);
effect.run(); // Starts collecting dependencies and executes, outputs: "Signal value: 0"
signal.value = 1; // Effect executes at the end of the current frameFor non-lazy FlutterEffects, run() re-executes and updates dependencies:
final signal = Signal(0);
final effect = FlutterEffect(() {
print('Signal value: ${signal.value}');
}); // Already executed and collected dependencies
effect.run(); // Re-executes, outputs: "Signal value: 0"Cleanup Functions
FlutterEffect supports registering cleanup functions that execute before Effect re-runs or when it's disposed:
final count = Signal(0);
FlutterEffect(() {
print('Count changed: ${count.value}');
final timer = Timer.periodic(Duration(seconds: 1), (_) {
count.value++;
});
onEffectCleanup(() => timer.cancel());
});Using in Async
If you need to use cleanup functions in async operations, you should directly use the effect.onCleanUp() method:
final effect = FlutterEffect(() async {
final subscription = await someAsyncOperation();
// In async context, directly use effect.onCleanUp()
effect.onCleanUp(() => subscription.cancel());
});Differences from Effect
The main difference between FlutterEffect and Effect is execution timing:
- Effect: Executes immediately when dependencies change (within reactive update cycle)
- FlutterEffect: Executes at the end of the current Flutter frame when dependencies change (batched)
Use Cases
Use FlutterEffect when:
- You need to execute UI-related side effects (such as updating UI state, showing dialogs, etc.)
- You want to merge multiple updates within the same frame into one execution
- You don't want to execute side effects during frame rendering
Use Effect when:
- You need to immediately respond to dependency changes
- You're executing non-UI-related side effects (such as logging, data synchronization, etc.)
- You don't need frame-level batching
Complete Examples
Batch Update Handling
final items = ListSignal([1, 2, 3]);
FlutterEffect(() {
// Even if items are modified multiple times in the same frame, this only executes once
print('Items updated: ${items.value}');
});
// Multiple modifications within the same frame
items.add(4);
items.add(5);
items.removeAt(0);
// Effect executes once at the end of the current frameUI State Updates
final isLoading = Signal(false);
final error = Signal<String?>(null);
FlutterEffect(() {
if (isLoading.value) {
// Show loading indicator
showLoadingDialog();
} else if (error.value != null) {
// Show error message
showErrorSnackBar(error.value!);
} else {
// Hide dialog
hideLoadingDialog();
}
});Combined with Cleanup Functions
final count = Signal(0);
Timer? _timer;
FlutterEffect(() {
_timer?.cancel();
_timer = Timer.periodic(Duration(seconds: 1), (_) {
count.value++;
});
onEffectCleanup(() {
_timer?.cancel();
_timer = null;
});
});Important Notes
Frame Scheduling:
FlutterEffectusesSchedulerBinding.instance.endOfFrameto schedule execution, ensuring execution after frame rendering completes.Batching: Multiple triggers within the same frame are automatically merged into one execution, improving performance.
Lifecycle:
FlutterEffectrequires manual lifecycle management. Remember to calldispose()when done.Dependency Tracking:
FlutterEffectautomatically tracks dependencies and schedules execution when dependencies change.Performance Optimization: For frequently updating scenarios, using
FlutterEffectcan significantly reduce execution count and improve performance.