select<T, R> method

R select<T, R>(
  1. R selector(
    1. T value
    )
)

Watch a value of type T exposed from a provider, and listen only partially to changes.

select must be used only inside the build method of a widget. It will not work inside other life-cycles, including State.didChangeDependencies.

By using select, instead of watching the entire object, the listener will rebuild only if the value returned by selector changes.

When a provider emits an update, it will call synchronously all selector.

Then, if they return a value different from the previously returned value, the dependent will be marked as needing to rebuild.

For example, consider the following object:

class Person with ChangeNotifier {
  String name;
  int age;

  // Add some logic that may update `name` and `age`
}

Then a widget may want to listen to a person's name without listening to its age.

This cannot be done using context.watch/Provider.of. Instead, we can use select, by writing the following:

Widget build(BuildContext context) {
  final name = context.select((Person p) => p.name);

  return Text(name);
}

It is fine to call select multiple times.

Implementation

R select<T, R>(R Function(T value) selector) {
  assert(widget is! SliverWithKeepAliveWidget, '''
  Tried to use context.select inside a SliverList/SliderGridView.

  This is likely a mistake, as instead of rebuilding only the item that cares
  about the selected value, this would rebuild the entire list/grid.

  To fix, add a `Builder` or extract the content of `itemBuilder` in a separate widget:

  ```dart
  ListView.builder(
    itemBuilder: (context, index) {
      return Builder(builder: (context) {
        final todo = context.select((TodoList list) => list[index]);
        return Text(todo.name);
      });
    },
  );
  ```
  ''');
  assert(widget is LayoutBuilder || debugDoingBuild, '''
Tried to use `context.select` outside of the `build` method of a widget.

Any usage other than inside the `build` method of a widget are not supported.
''');
  final inheritedElement = Provider._inheritedElementOf<T>(this);
  try {
    final value = inheritedElement.value;
    assert(() {
      _debugIsSelecting = true;
      return true;
    }());
    final selected = selector(value);
    dependOnInheritedElement(
      inheritedElement,
      aspect: (T newValue) => !const DeepCollectionEquality()
          .equals(selector(newValue), selected),
    );
    return selected;
  } finally {
    assert(() {
      _debugIsSelecting = false;
      return true;
    }());
  }
}