kaeru 0.2.6
kaeru: ^0.2.6 copied to clipboard
A powerful a comprehensive and efficient reactivity system for Flutter, inspired by Vue 3's @vue/reactivity.
import 'package:flutter/material.dart';
import 'package:kaeru/kaeru.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("Kaeru Example")),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(children: [
// TextButton(
// child: Text('refresh'), onPressed: () => setState(() {})),
// counterDefine((onMax: () => print('OK'))),
ParentWidget(),
]))));
}
}
class App2 extends StatelessWidget {
final showCounter = Ref(true);
App2({super.key});
@override
Widget build(context) {
return Watch(() => Row(children: [
ElevatedButton(
onPressed: () {
showCounter.value = !showCounter.value;
},
child: Text(showCounter.value ? 'hide' : 'show'),
),
if (showCounter.value) Counter()
]));
}
}
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> with KaeruMixin, KaeruLifeMixin {
late final foo = ref<int>(0);
late final fooDouble = computed(() => foo.value * 2);
late final fooDoublePlus = Computed<int>(() => foo.value + 1);
late final fooGtTeen = computed<bool>(() {
print('Computed call');
return fooDouble.value > 10;
});
late final computedOnlyListen = computed(() => foo.value);
final bar = Ref<int>(0);
@override
void initState() {
watchEffect(() {
print('watchEffect run');
if (fooDoublePlus.value % 2 == 0) return;
print('foo + bar = ${foo.value + bar.value}');
});
watch([computedOnlyListen], () {
print('computed only listen changed');
});
onMounted(() => print('✅ Widget Mounted!'));
onDependenciesChanged(() => print('🔄 Dependencies Changed!'));
onUpdated((_) => print('♻️ Widget Updated!'));
onDeactivated(() => print('⚠️ Widget Deactivated!'));
onBeforeUnmount(() => print('🗑 Widget Disposed!'));
super.initState();
}
@override
Widget build(BuildContext context) {
super.build(context);
print('Root render');
return Column(
children: [
ElevatedButton(
onPressed: () {
foo.value++;
},
child: const Text("Increase foo"),
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: () {
bar.value++;
},
child: const Text("Increase bar"),
),
const SizedBox(height: 16),
Watch(() {
print('Watch render');
if (fooGtTeen.value) {
return Watch(() {
print('Watch child 1 render');
return Text("Bar: ${bar.value}");
});
} else {
return Text("Bar: ${bar.value}");
}
}),
Watch(() {
print('Widget parent ShowFoo render');
return bar.value % 2 == 0 ? SizedBox.shrink() : ShowFoo(foo: foo);
})
],
);
}
}
class ShowFoo extends StatefulWidget {
final Ref<int> foo;
const ShowFoo({super.key, required this.foo});
@override
createState() => _ShowFooState();
}
class _ShowFooState extends State<ShowFoo> with KaeruListenMixin, KaeruMixin {
late final _fooDouble = computed(() {
print('ShowFoo computed emit change');
return widget.foo.value * 2;
});
@override
void initState() {
listen(widget.foo, () {
print('ShowFoo emit change foo ${widget.foo.value}');
});
super.initState();
}
@override
Widget build(context) {
return Column(children: [
Watch(() => Text('ShowFoo: ${widget.foo.value}')),
Watch(() => Text('foo * 2 = ${_fooDouble.value}'))
]);
}
}
///////////////////////////// test usePick
class TestUsePick extends StatefulWidget {
@override
createState() => _TestUsePickState();
}
class _TestUsePickState extends State<TestUsePick> with KaeruMixin {
late final map = ref({'counter': 0, 'bar': 0});
@override
Widget build(context) {
return ListView(
children: [
Row(children: [
Watch(() {
final counter = usePick(() => map.value['counter']);
print('renfd');
return Text('counter = ${counter.value}');
}),
Watch(() {
print('renfd bar');
return Text('bar = ${map.value['bar']}');
})
]),
TextButton(
onPressed: () =>
map.value = {...map.value, 'counter': map.value['counter']! + 1},
child: Text('counter++'),
),
TextButton(
onPressed: () =>
map.value = {...map.value, 'bar': map.value['bar']! + 1},
child: Text('bar++'),
)
],
);
}
}
//============================= test define_widget //
// typedef FooProps = ({Ref<int> counter});
// // ignore: non_constant_identifier_names
// final Foo = defineWidget((FooProps props) {
// final foo = $ref(0);
// void onPressed() {
// foo.value++;
// }
// void resetParent() {
// props.counter.value = 0;
// }
// return (ctx) => Row(
// children: [
// TextButton(
// onPressed: onPressed,
// child:
// Text('counter + foo = ${props.counter.value + foo.value}')),
// TextButton(
// onPressed: resetParent, child: Text('Reset counter parent'))
// ],
// );
// });
// typedef CounterProps = ({VoidCallback onMax});
// final counterDefine = defineWidget((CounterProps props) {
// final counter = $ref(0);
// final (isHover: isHover, hoverWrap: hoverWrap) = useHoverWidget();
// print('Render once');
// void onPressed() {
// counter.value++;
// if (counter.value > 10) props.onMax();
// }
// return (ctx) => Row(
// children: [
// TextButton(
// onPressed: onPressed, child: Text('counter = $counter.value')),
// Text('Foo is hoving: ${isHover.value}',
// style: TextStyle(color: Colors.red)),
// hoverWrap((_) => Foo((counter: counter)))
// ],
// );
// });
// ============== test KaeruWidget ==========
class CounterWidget extends KaeruWidget<CounterWidget> {
final int depend;
const CounterWidget({super.key, required this.depend});
@override
Setup setup() {
final count = ref(0);
final depend = prop((w) => w.depend);
watchEffect(() {
count.value; // track
debugPrint('effect in count changed ${count.value}');
});
watchEffect(() {
depend.value; // track
debugPrint('effect in depend changed ${depend.value}');
});
onMounted(() {
debugPrint('mounted');
});
return () {
debugPrint('main counter re-build');
return Row(children: [
Watch(() {
debugPrint('depend in counter changed');
return Text('depend = ${depend.value}');
}),
Watch(() {
debugPrint('counter in counter changed');
return Text('counter = ${count.value}');
}),
IconButton(onPressed: () => count.value++, icon: const Icon(Icons.add)),
IconButton(
onPressed: () => count.value--, icon: const Icon(Icons.remove)),
]);
};
}
}
class ParentWidget extends StatefulWidget {
const ParentWidget({super.key});
@override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
int depend = 0;
@override
Widget build(BuildContext context) {
return Row(children: [
CounterWidget(depend: depend),
ElevatedButton(
onPressed: () {
setState(() {
depend++;
});
},
child: Text('depend++ ${depend}'))
]);
}
}