Skip to content

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():

dart
// 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 ChangeNotifier or ValueNotifier
  • 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:

dart
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 Listenable objects
  • 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():

dart
// 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_it
  • watchValue() 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:

dart
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.length is 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:

dart
R watchPropertyValue<T extends Listenable, R>(
  R Function(T) selector,
  {String? instanceName, GetIt? getIt}
)
dart
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:

dart
// 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

dart
// 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():

dart
final data = watchValue((DataManager m) => m.data);

If the whole object can be updated or many properties can change:

Use ChangeNotifier and watchIt():

dart
final manager = watchIt<CounterModel>();

Or if performance is important, use watchPropertyValue() for selective updates:

dart
// Only rebuild when THIS specific property changes
final darkMode = watchPropertyValue((SettingsModel m) => m.darkMode);

For local Listenables not registered in get_it:

Use watch():

dart
final text = watch(controller).value.text;

Practical Example

Mixing different watch functions:

dart
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

Released under the MIT License.