Agent skill
signals-and-flutter-hooks
Explore state management in Flutter, from the basics of `setState` to advanced techniques using ValueNotifier, Signals, Flutter Hooks, and the new signals_hooks package for a reactive and efficient approach.
Install this agent skill to your Project
npx add-skill https://github.com/rodydavis/skills/tree/main/skills/signals-and-flutter-hooks
Metadata
Additional technical details for this skill
- last modified
- Tue, 03 Feb 2026 20:04:15 GMT
SKILL.md
Signals and Flutter Hooks
When working with data in Flutter, on of the first things you are exposed to is setState.
setState
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int count = 0;
void increment() {
if (mounted) {
setState(() {
count++;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(child: Text('Count: $count')),
floatingActionButton: FloatingActionButton(
onPressed: increment,
child: const Icon(Icons.add),
),
);
}
}
This simply marks the widget as dirty every time you call setState but requires you (as the developer) to be mindful and explict about when those updates happen. If you forget to call setState when mutating data the widget tree can become stale.
ValueNotifier
We can impove this by using ValueNotifier instead of storing the value directly. This gives us the ability to read and write a value in a container and use helper widgets like ValueListenableBuilder to update sub parts of the widget tree on value changes.
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
final count = ValueNotifier(0);
void increment() {
count.value++;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(child: ValueListenableBuilder(
valueListenable: count,
builder: (context, value, child) {
return Text('Count: $value');
}
)),
floatingActionButton: FloatingActionButton(
onPressed: increment,
child: const Icon(Icons.add),
),
);
}
}
FlutterSignal
Using the signals package we can upgrade ValueNotifier to a signal backed implmentation which uses a reactive graph based on a push / pull architecture.
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
void main() {
runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
final count = signal(0);
void increment() {
count.value++;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(
child: ValueListenableBuilder(
valueListenable: count,
builder: (context, value, child) {
return Text('Count: $value');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: increment,
child: const Icon(Icons.add),
),
);
}
}
Signals created after 6.0.0 also implement ValueNotifier so you can easily migrate them without changing any other code.
Instead of ValueListenableBuilder we can use the Watch widget or .watch(context) extension.
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
void main() {
runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
final count = signal(0);
void increment() {
count.value++;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(
child: Text('Count: ${count.watch(context)}'),
),
floatingActionButton: FloatingActionButton(
onPressed: increment,
child: const Icon(Icons.add),
),
);
}
}
flutter_hooks
Using Flutter Hooks we can reduce boilerplate of StatefulWidget by switching to a HookWidget. With useState we can define the state directly in the build method and easily share them across widgets.
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
void main() {
runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}
class Counter extends HookWidget {
const Counter({super.key});
@override
Widget build(BuildContext context) {
final count = useState(0);
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(child: Text('Count: ${count.value}')),
floatingActionButton: FloatingActionButton(
onPressed: () => count.value++,
child: const Icon(Icons.add),
),
);
}
}
useState returns a ValueNotifier that automatically rebuilds the widget on changes
signals_hooks
Using a new package signals_hooks we can now define signals in HookWidgets and have the benifits of a reactive graph with shareable lifecycles between widgets.
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:signals_hooks/signals_hooks.dart';
void main() {
runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Counter()));
}
class Counter extends HookWidget {
const Counter({super.key});
@override
Widget build(BuildContext context) {
final count = useSignal(0);
final countStr = useComputed(() => count.value.toString());
useSignalEffect(() {
print('count: $count');
});
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(child: Text('Count: $countStr')),
floatingActionButton: FloatingActionButton(
onPressed: () => count.value++,
child: const Icon(Icons.add),
),
);
}
}
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
how-to-do-offline-recommendations-with-sqlite-and-gemini
Learn how to enhance your CMS like PocketBase with AI-powered content recommendations using text embeddings, SQLite, and k-nearest neighbor search for efficient and scalable related content suggestions.
how-to-build-a-webrtc-signal-server-with-pocketbase
Learn how to build a simple WebRTC video call application using PocketBase as a signaling server, enabling peer-to-peer communication with SQLite on the server and realtime updates via Server Sent Events.
flutter-terminal-cheat-sheet
This post provides a handy collection of Flutter commands and scripts for web development, package creation, troubleshooting, testing, and more, streamlining your Flutter workflow.
how-to-print-multiple-objects-to-the-console-with-print-in-dart
Learn how to print multiple objects to the console in Dart using Records, offering a similar experience to JavaScript's `console.log()` functionality.
flutter-input-output-preview
Build responsive Flutter apps with a reusable `TwoPane` widget and an `InputOutputPreview` component for side-by-side code and preview display on both mobile and desktop.
displaying-html-in-flutter
Easily display and interact with HTML content in your Flutter app using the `easy_web_view` package, which supports both web and mobile platforms.
Didn't find tool you were looking for?