Clarify performacne of SingleTickerProviderStateMixin vs TickerProviderStateMixin (#164870)

Adds the following line to
[TickerProviderStateMixin](https://api.flutter.dev/flutter/widgets/TickerProviderStateMixin-mixin.html):

>Using one [TickerProviderStateMixin] twice is more efficient than two
[SingleTickerProviderStateMixin]s.

This is based on a discussion on Flutter Discord with @chunhtai. From
what I understood, creating multiple `SingleTickerProviderStateMixin`s
is more expensive because you need two different state which is
relatively costly compared to having just one. Not sure if this line
should should be qualified like "Using one [TickerProviderStateMixin]
twice is *generally* more efficient than two
[SingleTickerProviderStateMixin]s" or similar.

This change should be reviewed for correctness as I don't actually
understand enough about the difference between
`TickerProviderStateMixin` and `SingleTickerProviderStateMixin`.

Closes #164869

<details>

<summary>
Example case where this was relevant (click)
</summary>

I was avoiding using `TickerProviderStateMixin` like so:

```dart
    _animationController = AnimationController.unbounded(vsync: this)
      ..addListener(() {
        _updateScale(_animationController.value);
      });
   
    //could have just set `vsync` to `this` if using `TickerProviderStateMixin`
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _verticalAnimationController =
          AnimationController.unbounded(vsync: _verticalController.position.context.vsync)
            ..addListener(() {
              _verticalController.jumpTo(_verticalAnimationController.value);
            });

      _horizontalAnimationController =
          AnimationController.unbounded(vsync: _horizontalController.position.context.vsync)
            ..addListener(() {
              _horizontalController.jumpTo(_horizontalAnimationController.value);
            });
    });
```

Instead of just:

```dart
    _animationController = AnimationController.unbounded(vsync: this)
      ..addListener(() {
        _updateScale(_animationController.value);
      });

    _verticalAnimationController = AnimationController.unbounded(vsync: this)
      ..addListener(() {
        _verticalController.jumpTo(_verticalAnimationController.value);
      });

    _horizontalAnimationController = AnimationController.unbounded(vsync: this)
      ..addListener(() {
        _horizontalController.jumpTo(_horizontalAnimationController.value);
      });
```

</details>

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
yakagami 2025-03-11 12:33:03 -04:00 committed by GitHub
parent 39dc5717d5
commit 7bf8837116
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -301,6 +301,11 @@ mixin SingleTickerProviderStateMixin<T extends StatefulWidget> on State<T>
/// If you only have a single [Ticker] (for example only a single /// If you only have a single [Ticker] (for example only a single
/// [AnimationController]) for the lifetime of your [State], then using a /// [AnimationController]) for the lifetime of your [State], then using a
/// [SingleTickerProviderStateMixin] is more efficient. This is the common case. /// [SingleTickerProviderStateMixin] is more efficient. This is the common case.
///
/// When creating multiple [AnimationController]s, using a single state with
/// [TickerProviderStateMixin] as vsync for all [AnimationController]s is more
/// efficient than creating multiple states with
/// [SingleTickerProviderStateMixin].
@optionalTypeArgs @optionalTypeArgs
mixin TickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider { mixin TickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider {
Set<Ticker>? _tickers; Set<Ticker>? _tickers;