solidart_hooks
For a comprehensive and updated documentation go to The Official Documentation
Helper library to make working with solidart in flutter_hooks easier.
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:solidart_hooks/solidart_hooks.dart';
class Example extends HookWidget {
const Example({super.key});
@override
Widget build(BuildContext context) {
final count = useSignal(0);
final doubleCount = useComputed(() => count.value * 2);
useSolidartEffect(() {
debugPrint('Effect count: ${count.value}, doubleCount: ${doubleCount.value}');
});
return Scaffold(
body: Center(
child: SignalBuilder(
builder: (context, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: ${count.value}'),
Text('Double: ${doubleCount.value}'),
],
);
}
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => count.value++,
child: const Icon(Icons.add),
),
);
}
}
As you can see, a SignalBuilder
is used to rebuild the widget when the signal changes.
This is a good practice to avoid rebuilding the entire widget tree when a signal changes, and only rebuild the parts that depend on the signal.
Alternatively, you can use useListenable
from flutter_hooks
to listen to the signal changes, but this will rebuild the entire widget tree; for example:
class UseSignalExample extends HookWidget {
const UseSignalExample({super.key});
@override
Widget build(BuildContext context) {
final count = useSignal(0);
// this will rebuild the entire widget when the signal changes
useListenable(count);
return Scaffold(
appBar: AppBar(title: const Text('useSignal')),
body: Center(child: Text('Count: ${count.value}')),
floatingActionButton: FloatingActionButton(
onPressed: () => count.value++,
child: const Icon(Icons.add),
),
);
}
}
useSignal
How to create a new signal inside of a hook widget:
class Example extends HookWidget {
@override
Widget build(BuildContext context) {
final count = useSignal(0);
return Scaffold(
body: Center(
child: SignalBuilder(
builder: (context, child) {
return Text('Count: ${count.value}');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => count.value++,
child: const Icon(Icons.add),
),
);
}
}
The widget will automatically rebuild when the value changes. The signal will get disposed when the widget gets unmounted.
useComputed
How to create a new computed signal inside of a hook widget:
class Example extends HookWidget {
@override
Widget build(BuildContext context) {
final count = useSignal(5);
final doubled = useComputed(() => count.value * 2);
return Scaffold(
body: Center(
child: SignalBuilder(
builder: (context, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: ${count.value}'),
Text('Doubled: ${doubled.value}'),
],
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => count.value++,
child: const Icon(Icons.add),
),
);
}
}
The widget will automatically rebuild when the value changes. The computed will get disposed when the widget gets unmounted.
useSolidartEffect
How to create a new effect inside of a hook widget:
class Example extends HookWidget {
@override
Widget build(BuildContext context) {
final count = useSignal(0);
useSolidartEffect(() {
debugPrint('Effect triggered! Count: ${count.value}');
});
return Scaffold(
body: Center(
child: SignalBuilder(
builder: (context, child) {
return Text('Count: ${count.value}');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => count.value++,
child: const Icon(Icons.add),
),
);
}
}
useListSignal
How to create a new list signal inside of a hook widget:
class Example extends HookWidget {
@override
Widget build(BuildContext context) {
final items = useListSignal<String>(['Item1', 'Item2']);
return Scaffold(
body: Center(
child: SignalBuilder(
builder: (context, child) {
return Text('Items: ${items.value.join(', ')}');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => items.add('Item${items.value.length + 1}'),
child: const Icon(Icons.add),
),
);
}
}
The widget will automatically rebuild when the list changes. The signal will get disposed when the widget gets unmounted.
useSetSignal
How to create a new set signal inside of a hook widget:
class Example extends HookWidget {
@override
Widget build(BuildContext context) {
final uniqueItems = useSetSignal<String>({'Item1', 'Item2'});
return Scaffold(
body: Center(
child: SignalBuilder(
builder: (context, child) {
return Text('Items: ${uniqueItems.value.join(', ')}');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => uniqueItems.add('Item${uniqueItems.value.length + 1}'),
child: const Icon(Icons.add),
),
);
}
}
The widget will automatically rebuild when the set changes. The signal will get disposed when the widget gets unmounted.
useMapSignal
How to create a new map signal inside of a hook widget:
class Example extends HookWidget {
@override
Widget build(BuildContext context) {
final userRoles = useMapSignal<String, String>({'admin': 'John'});
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SignalBuilder(
builder: (context, child) {
return Text(
'Roles: ${userRoles.value.entries.map((e) => '${e.key}:${e.value}').join(', ')}',
);
},
),
],
),
),
floatingActionButton: FloatingActionButton(
floatingActionButton: FloatingActionButton(
onPressed: () => userRoles['user${userRoles.value.length}'] = 'User${userRoles.value.length}',
child: const Icon(Icons.add),
),
);
}
}
The widget will automatically rebuild when the map changes. The signal will get disposed when the widget gets unmounted.
useResource
How to create a new resource inside of a hook widget:
class Example extends HookWidget {
@override
Widget build(BuildContext context) {
final userResource = useResource(() async {
await Future.delayed(const Duration(seconds: 1));
return 'Data loaded';
});
return Scaffold(
body: Center(
child: SignalBuilder(
builder: (context, child) {
return userResource.state.on(
ready: (data) => Text('Result: $data'),
error: (error, stackTrace) => Text('Error: $error'),
loading: () => const CircularProgressIndicator(),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => userResource.refresh(),
child: const Icon(Icons.refresh),
),
);
}
}
The resource will get disposed when the widget gets unmounted.
useResourceStream
How to create a new resource from a stream inside of a hook widget:
class Example extends HookWidget {
@override
Widget build(BuildContext context) {
final streamResource = useResourceStream<int>(() {
return Stream.periodic(const Duration(seconds: 1), (count) => count);
});
return Scaffold(
body: Center(
child: SignalBuilder(
builder: (context, child) {
return streamResource.state.on(
ready: (data) => Text('Stream value: $data'),
error: (error, stackTrace) => Text('Error: $error'),
loading: () => const CircularProgressIndicator(),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => streamResource.refresh(),
child: const Icon(Icons.refresh),
),
);
}
}
The resource will get disposed when the widget gets unmounted.
useExistingSignal
How to bind an existing signal inside of a hook widget:
class UseExistingSignalExample extends StatefulHookWidget {
const UseExistingSignalExample({super.key});
@override
State<UseExistingSignalExample> createState() =>
_UseExistingSignalExampleState();
}
class _UseExistingSignalExampleState extends State<UseExistingSignalExample> {
final existingSignal = Signal(42);
@override
Widget build(BuildContext context) {
final boundSignal = useExistingSignal(existingSignal);
return Scaffold(
body: Center(
child: SignalBuilder(
builder: (context, child) {
return Text('Value: ${boundSignal.value}');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => existingSignal.value++,
child: const Icon(Icons.add),
),
);
}
@override
void dispose() {
existingSignal.dispose();
super.dispose();
}
}
The signal will NOT get disposed when the widget gets unmounted (unless autoDispose is true).
Libraries
- solidart_hooks
- A Dart library for Solidart Hooks, providing Flutter Hooks bindings for Solidart.