More Watch Functions
You've learned watchValue() for watching ValueListenable properties. Now let's explore the other watch functions.
watchIt - Watch Whole Object in get_it
When your registered object IS a Listenable, use watchIt():
// Manager that IS a ChangeNotifier
class TodoManagerChangeNotifier extends ChangeNotifier {
List<String> _todos = [];
List<String> get todos => _todos;
void addTodo(String todo) {
_todos.add(todo);
notifyListeners(); // Notify on changes
}
}
// Watch the whole manager
class TodoList extends WatchingWidget {
@override
Widget build(BuildContext context) {
final manager = watchIt<TodoManagerChangeNotifier>();
return ListView.builder(
itemCount: manager.todos.length,
itemBuilder: (context, index) => Text(manager.todos[index]),
);
}
}When to use watchIt():
- Your object extends
ChangeNotifierorValueNotifier - You need to call methods on the object
- The whole object notifies changes
watch - Watch Any Listenable
watch() is the most flexible - watch ANY Listenable:
class CounterWidget extends WatchingWidget {
const CounterWidget({super.key, required this.counter});
final ValueNotifier<int> counter;
@override
Widget build(BuildContext context) {
// Watch the counter passed as parameter (not from get_it)
final count = watch(counter).value;
return Column(
children: [
Text('Count: $count'),
ElevatedButton(
onPressed: () => counter.value++,
child: Text('Increment'),
),
],
);
}
}When to use watch():
- Watching local
Listenableobjects - You already have a reference to the
Listenable - Most generic case
watch() is the Foundation
watch() is the most flexible function - you could use it to replace watchIt() and watchValue():
// These are equivalent:
final manager = watchIt<CounterManager>();
final manager = watch(di<CounterManager>());
// These are equivalent:
final count = watchValue((CounterManager m) => m.count);
final count = watch(di<CounterManager>().count).value;Why use the convenience functions?
watchIt()is cleaner for getting the whole object from get_itwatchValue()provides better type inference and cleaner syntax- Each is optimized for its specific use case
Using watch() Just to Trigger Rebuilds
Sometimes you don't need the return value - you just want to trigger a rebuild when a Listenable changes:
class FormWidget extends WatchingWidget {
const FormWidget({super.key, required this.controller});
final TextEditingController controller;
@override
Widget build(BuildContext context) {
// Watch the controller just to trigger rebuild - don't need the return value
watch(controller);
// Now we can use the controller's current state in our widget tree
return Column(
children: [
TextField(controller: controller),
Text('Character count: ${controller.text.length}'),
ElevatedButton(
onPressed: controller.text.isEmpty ? null : () => print('Submit'),
child: Text('Submit'),
),
],
);
}
}Key points:
watch(controller)triggers rebuild when controller notifies- We don't use the return value - just call
watch()for the side effect - The widget rebuilds, so
controller.text.lengthis always current - Button enable/disable state updates automatically
watchPropertyValue - Selective Updates
Only rebuilds when a specific property of a Listenable parent Object changes:
Method signature:
R watchPropertyValue<T extends Listenable, R>(
R Function(T) selector,
{String? instanceName, GetIt? getIt}
)class ThemeSwitch extends WatchingWidget {
@override
Widget build(BuildContext context) {
// Only rebuilds when darkMode changes, not language or fontSize
final darkMode = watchPropertyValue((SettingsModel m) => m.darkMode);
return Switch(
value: darkMode,
onChanged: (value) => di<SettingsModel>().setDarkMode(value),
);
}
}The difference:
// Rebuilds on EVERY SettingsModel change
final settings = watchIt<SettingsModel>();
final darkMode1 = settings.darkMode;
// Rebuilds ONLY when darkMode changes
final darkMode2 = watchPropertyValue((SettingsModel m) => m.darkMode);Quick Comparison
// 1. watchValue - Watch ValueListenable property from get_it
final todos = watchValue((TodoManager m) => m.todos);
// 2. watchIt - When manager is a Listenable registered in get_it
final manager = watchIt<CounterModel>();
// 3. watch - Local or direct Listenable
final counter = createOnce(() => ValueNotifier(0));
final count = watch(counter).value;
// 4. watchPropertyValue - Selective updates from Listenable registered in get_it
final darkMode = watchPropertyValue((SettingsModel m) => m.darkMode);Choosing the Right Function
If you have only one or two properties that should trigger an update:
Use ValueNotifier for each property and watchValue():
final data = watchValue((DataManager m) => m.data);If the whole object can be updated or many properties can change:
Use ChangeNotifier and watchIt():
final manager = watchIt<CounterModel>();Or if performance is important, use watchPropertyValue() for selective updates:
// Only rebuild when THIS specific property changes
final darkMode = watchPropertyValue((SettingsModel m) => m.darkMode);For local Listenables not registered in get_it:
Use watch():
final text = watch(controller).value.text;Practical Example
Mixing different watch functions:
class Dashboard extends WatchingWidget {
@override
Widget build(BuildContext context) {
// watchValue - for ValueListenable property
final userName = watchValue((SimpleUserManager m) => m.name);
// watchPropertyValue - selective rebuild
final darkMode = watchPropertyValue((SettingsModel m) => m.darkMode);
// watch - local state
final searchQuery = createOnce(() => ValueNotifier<String>(''));
final query = watch(searchQuery).value;
return Scaffold(
appBar: AppBar(
title: Text('Hello, $userName'),
backgroundColor: darkMode ? Colors.black : Colors.blue,
),
body: Column(
children: [
TextField(
onChanged: (value) => searchQuery.value = value,
),
Text('Searching: $query'),
],
),
);
}
}Key Takeaways
✅ watchValue() - Watch ValueListenable properties from get_it (one or two properties) ✅ watchIt() - Watch whole Listenable objects from get_it (many properties change) ✅ watchPropertyValue() - Selective updates from Listenable in get_it (performance optimization) ✅ watch() - Most flexible, any Listenable (local or parameter) ✅ Choose based on property count and update patterns ✅ Mix and match based on your needs
Next: Learn about watching multiple values.
See Also
- Your First Watch Functions - Start here
- Watching Multiple Values - Strategies for combining values
- Watching Streams & Futures - Streams and Futures
- Watch Functions Reference - Complete API