kaeru 0.1.7
kaeru: ^0.1.7 copied to clipboard
A powerful a comprehensive and efficient reactivity system for Flutter, inspired by Vue 3's @vue/reactivity.
Kaeru for Flutter #
Kaeru is a comprehensive and efficient reactivity system for Flutter, inspired by Vue 3's @vue/reactivity. It provides a fully functional reactive programming model that makes state management in Flutter simple, optimized, and declarative.
π Features #
- Fully reactive state management with
Ref,Computed,AsyncComputed, andwatchEffect. - Automatic dependency tracking for efficient updates.
- Supports both synchronous and asynchronous computed values.
- Optimized UI updates with
WatchandKaeruMixin. - Seamless integration with ChangeNotifier and ValueNotifier.
π¦ Installation #
Add this package to your pubspec.yaml:
dependencies:
kaeru:
git:
url: https://github.com/tachibana-shin/flutter_kaeru.git
Import it in your project:
import 'package:kaeru/kaeru.dart';
π API Documentation #
1οΈβ£ Reactive State: Ref<T> #
Represents a reactive variable that automatically triggers updates when changed.
Parameters:
| Parameter | Type | Description |
|---|---|---|
value |
T |
The initial value of the reactive reference. |
Methods:
| Method | Returns | Description |
|---|---|---|
select<U>(U Function(T value)) |
Computed<U> |
Creates a computed value derived from this Ref<T>. |
Example:
final count = Ref(0);
count.addListener(() {
print("Count changed: ${count.value}");
});
count.value++; // β
Triggers update
final doubleCount = count.select((v) => v * 2);
print(doubleCount.value); // β
0
count.value = 5;
print(doubleCount.value); // β
10
2οΈβ£ Derived State: Computed<T> #
Creates a computed value that automatically updates when dependencies change.
Parameters:
| Parameter | Type | Description |
|---|---|---|
getter |
T Function() |
A function that returns the computed value. |
Methods:
| Method | Returns | Description |
|---|---|---|
select<U>(U Function(T value)) |
Computed<U> |
Creates a derived computed value. |
Example:
final count = Ref(2);
final doubleCount = Computed(() => count.value * 2);
print(doubleCount.value); // β
4
count.value++;
print(doubleCount.value); // β
6
final tripleCount = doubleCount.select((v) => v * 1.5);
print(tripleCount.value); // β
9
3οΈβ£ Effects: watchEffect & watch #
watchEffect(Function callback) -> VoidCallback
- Automatically tracks dependencies and re-executes when values change.
Example:
final stop = watchEffect(() {
print("Count is now: ${count.value}");
});
count.value++; // β
Automatically tracks dependencies
stop(); // β
Stops watching
watch(List<ChangeNotifier> sources, Function callback, {bool immediate = false}) -> VoidCallback
- Watches multiple
ChangeNotifiersources. - If
immediate = true, executes the callback immediately.
Example:
final stop = watch([count], () {
print("Count changed: ${count.value}");
}, immediate: true);
stop(); // β
Stops watching
4οΈβ£ Asynchronous Derived State: AsyncComputed<T> #
Handles computed values that depend on asynchronous operations.
Parameters:
| Parameter | Type | Description |
|---|---|---|
getter |
Future<T> Function() |
A function returning a future value. |
defaultValue |
T? |
An optional initial value before computation completes. |
beforeUpdate |
T? Function() |
An optional function to run before updating the value. |
notifyBeforeUpdate |
bool = true |
Whether to notify listeners before updating the value. |
onError |
Function(dynamic error)? |
An optional error handler. |
immediate |
bool |
Whether to compute immediately. |
Example:
final asyncData = AsyncComputed(() async {
await Future.delayed(Duration(seconds: 1));
return "Loaded";
}, defaultValue: "Loading", onError: (e) => print("Error: $e"), immediate: true);
print(asyncData.value); // β
"Loading"
await Future.delayed(Duration(seconds: 1));
print(asyncData.value); // β
"Loaded"
5οΈβ£ UI Integration: KaeruMixin and Watch #
KaeruMixin (StatefulWidget Integration)
Allows stateful widgets to easily integrate with reactive values.
Example:
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> with KaeruMixin {
late final Ref<int> count;
@override
void initState() {
super.initState();
count = ref(0);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Watch(() => Text("Count: ${count.value}")),
ElevatedButton(
onPressed: () => count.value++,
child: Text("Increment"),
),
],
);
}
}
Watch (Automatic UI Rebuilds)
A widget that automatically updates when its dependencies change.
Example:
Watch(
() => Text("Value: ${count.value}"),
)
6οΈβ£ Integration with ValueNotifier & ChangeNotifier #
ValueNotifier.toRef()
Converts a ValueNotifier<T> into a Ref<T>.
Example:
final valueNotifier = ValueNotifier(0);
final ref = valueNotifier.toRef();
ref.addListener(() {
print("Updated: ${ref.value}");
});
valueNotifier.value = 10; // β
Ref updates automatically
ValueNotifier Extension
Adds .toRef() to ValueNotifier to integrate seamlessly.
π Kaeru Lifecycle & Listening Mixins #
KaeruLifeMixin and KaeruListenMixin are powerful mixins designed to simplify Flutter development by providing Vue-like lifecycle hooks and reactive state listening.
π― Why Use These Mixins? #
β Cleaner code: No need to override multiple lifecycle methods or manage listeners manually. β Reusable: Apply them to any StatefulWidget to enhance reactivity. β Inspired by Vue: Provides a familiar development experience for reactive state management.
π’ KaeruLifeMixin #
KaeruLifeMixin provides Vue-like lifecycle hooks for StatefulWidget. It enables multiple callbacks for different lifecycle events.
π Features
onMounted(): Called when the widget is first created (initState).onDependenciesChanged(): Called when dependencies change (didChangeDependencies).onUpdated(): Called when the widget receives updated properties (didUpdateWidget).onDeactivated(): Called when the widget is temporarily removed (deactivate).onBeforeUnmount(): Called just before the widget is disposed (dispose).
π Example Usage
class MyComponent extends StatefulWidget {
@override
_MyComponentState createState() => _MyComponentState();
}
class _MyComponentState extends State<MyComponent> with KaeruLifeMixin<MyComponent> {
@override
void initState() {
super.initState();
onMounted(() => print('β
Widget Mounted!'));
onDependenciesChanged(() => print('π Dependencies Changed!'));
onUpdated(() => print('β»οΈ Widget Updated!'));
onDeactivated(() => print('β οΈ Widget Deactivated!'));
onBeforeUnmount(() => print('π Widget Disposed!'));
}
@override
Widget build(BuildContext context) {
return Text('KaeruLifeMixin Example');
}
}
π’ KaeruListenMixin #
KaeruListenMixin simplifies listening to ChangeNotifier updates within a StatefulWidget. It allows adding listeners dynamically and managing their cleanup automatically.
π Features
listen(): Subscribes to a singleChangeNotifierand executes a callback when it changes.listenAll(): Subscribes to multipleChangeNotifierswith a single callback.- Returns a cancel function to remove listeners when necessary.
π Example Usage
Listening to a Single Notifier
class MyNotifier extends ChangeNotifier {
void update() {
notifyListeners();
}
}
class MyComponent extends StatefulWidget {
@override
_MyComponentState createState() => _MyComponentState();
}
class _MyComponentState extends State<MyComponent> with KaeruListenMixin<MyComponent> {
final myNotifier = MyNotifier();
VoidCallback? cancelListener;
@override
void initState() {
super.initState();
cancelListener = listen(myNotifier, () {
print('Single notifier changed!');
});
}
@override
void dispose() {
cancelListener?.call();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Text('Listening to a single ChangeNotifier');
}
}
Listening to Multiple Notifiers
class NotifierA extends ChangeNotifier {
void update() => notifyListeners();
}
class NotifierB extends ChangeNotifier {
void update() => notifyListeners();
}
class MyComponent extends StatefulWidget {
@override
_MyComponentState createState() => _MyComponentState();
}
class _MyComponentState extends State<MyComponent> with KaeruListenMixin<MyComponent> {
final notifierA = NotifierA();
final notifierB = NotifierB();
VoidCallback? cancelListeners;
@override
void initState() {
super.initState();
cancelListeners = listenAll([notifierA, notifierB], () {
print('One of the notifiers changed!');
});
}
@override
void dispose() {
cancelListeners?.call();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Text('Listening to multiple ChangeNotifiers');
}
}
β¨ Summary #
| Feature | KaeruLifeMixin | KaeruListenMixin |
|---|---|---|
| Lifecycle Hooks | β
Provides onMounted, onUpdated, onBeforeUnmount, etc. |
β Not applicable |
| Reactive Listeners | β Not applicable | β
Handles ChangeNotifier updates |
| Automatic Cleanup | β Hooks are executed at proper lifecycle stages | β Listeners are removed automatically |
| Code Simplicity | β Reduces the need for overriding multiple lifecycle methods | β
Manages ChangeNotifier subscriptions easily |
π KaeruLifeMixin is perfect for handling widget lifecycle events.
π KaeruListenMixin makes managing ChangeNotifier listeners easy.
π― API Summary #
| Feature | Supported |
|---|---|
Ref<T> |
β |
Computed<T> |
β |
AsyncComputed<T> |
β |
watchEffect |
β |
watch |
β |
KaeruMixin |
β |
Watch Widget |
β |
ValueNotifier.toRef() |
β |
ReactiveNotifier<T> |
β |
VueNotifier.toRef() |
β |
This package provides an intuitive and efficient reactivity system for Flutter, making state management much easier and more performant. π
π Contributing #
Pull requests and feature requests are welcome! Feel free to open an issue or contribute.
π License #
MIT License. See LICENSE for details.