Repository: flutter
Issue: Feature request: Make listeners removable inside function body.
Labels: c: new feature,framework,c: API break,c: proposal,customer: wednesday,P3,team-framework,triaged-framework

## Summary

Add a way to remove a controller's listener inside the listener's function body for cleaner code.

## Use case

A use case a lot of community members and I have come across is when you want to conditionally cancel a subscription when results are coming in, ie., inside the listener's function body. This means you would either have to declare the listener beforehand so there's a reference to the listener that you can pass to the controller's `removeListener` method, or call a function outside the function body that has access to this listener. Both have negative consequences; in the first approach, the listener object will be mutable, and in the latter, two functions need to have access to this listener object which adds a preventable side-effect.

In our experience, this problem is most prominent in animation listeners and stream listeners.

Example of how it's done currently using `AnimationStatusListeners`:
```dart
Future<void> show() {
  final completer = Completer();
  AnimationStatusListener listener;
  listener = (AnimationStatus status) {
    if(status == AnimationStatus.dismissed) {
      completer.complete();
      _controller.removeStatusListener(listener);
    }
  };
  _controller.addStatusListener(listener);
  return completer.future;
}
```

## Proposal

### Approach A (breaking)
A `VoidCallback` function that's passed to the listener called something along the lines of `removeSelf`. This function can be called inside the listener's function body and will cancel the subscription.

Example of the proposed approach using `AnimationStatusListeners`:
```dart
Future<void> show() {
  final completer = Completer();
  _controller.addStatusListener((AnimationStatus status, VoidCallback removeSelf) {
    if(status == AnimationStatus.dismissed) {
      completer.complete();
      removeSelf();
    }
  });
  return completer.future;
}
```

### Approach B (non-breaking)
Add a method, `listenUntil` for example, that adds a listener that automatically cancels itself as soon as it returns true. Alternatively, add an optional parameter to existing `addListener` functions called `until` of type `bool Function(MyType)`.

Example of an approach where a new method, `addStatusListenerUntil` is added:
```dart
Future<void> show() {
  final completer = Completer();
  _controller.addStatusListenerUntil((AnimationStatus status) {
    if(status == AnimationStatus.dismissed) {
      completer.complete();
      return true;
    }
    return false;
  });
  return completer.future;
}
```

Example of an approach that adds an optional parameter to the existing `addListener` methods:
```dart
  Future<void> show() {
    final completer = Completer();
    _controller.addStatusListener(
      (AnimationStatus status) {
        if (status == AnimationStatus.dismissed) {
          completer.complete();
        }
      },
      until: (AnimationStatus status) {
        completer.isCompleted;
      },
    );
    return completer.future;
  }
```