Florian

Add GetTickerProviderStateMixin when multiple AnimationController objects are used

@@ -36,13 +36,13 @@ mixin GetSingleTickerProviderStateMixin on GetxController @@ -36,13 +36,13 @@ mixin GetSingleTickerProviderStateMixin on GetxController
36 if (_ticker == null) return true; 36 if (_ticker == null) return true;
37 throw FlutterError.fromParts(<DiagnosticsNode>[ 37 throw FlutterError.fromParts(<DiagnosticsNode>[
38 ErrorSummary( 38 ErrorSummary(
39 - '$runtimeType is a SingleTickerProviderStateMixin but multiple tickers were created.'), 39 + '$runtimeType is a GetSingleTickerProviderStateMixin but multiple tickers were created.'),
40 ErrorDescription( 40 ErrorDescription(
41 - 'A SingleTickerProviderStateMixin can only be used as a TickerProvider once.'), 41 + 'A GetSingleTickerProviderStateMixin can only be used as a TickerProvider once.'),
42 ErrorHint( 42 ErrorHint(
43 'If a State is used for multiple AnimationController objects, or if it is passed to other ' 43 'If a State is used for multiple AnimationController objects, or if it is passed to other '
44 'objects and those objects might use it more than one time in total, then instead of ' 44 'objects and those objects might use it more than one time in total, then instead of '
45 - 'mixing in a SingleTickerProviderStateMixin, use a regular TickerProviderStateMixin.', 45 + 'mixing in a GetSingleTickerProviderStateMixin, use a regular GetTickerProviderStateMixin.',
46 ), 46 ),
47 ]); 47 ]);
48 }()); 48 }());
@@ -66,7 +66,7 @@ mixin GetSingleTickerProviderStateMixin on GetxController @@ -66,7 +66,7 @@ mixin GetSingleTickerProviderStateMixin on GetxController
66 throw FlutterError.fromParts(<DiagnosticsNode>[ 66 throw FlutterError.fromParts(<DiagnosticsNode>[
67 ErrorSummary('$this was disposed with an active Ticker.'), 67 ErrorSummary('$this was disposed with an active Ticker.'),
68 ErrorDescription( 68 ErrorDescription(
69 - '$runtimeType created a Ticker via its SingleTickerProviderStateMixin, but at the time ' 69 + '$runtimeType created a Ticker via its GetSingleTickerProviderStateMixin, but at the time '
70 'dispose() was called on the mixin, that Ticker was still active. The Ticker must ' 70 'dispose() was called on the mixin, that Ticker was still active. The Ticker must '
71 'be disposed before calling super.dispose().', 71 'be disposed before calling super.dispose().',
72 ), 72 ),
@@ -82,6 +82,99 @@ mixin GetSingleTickerProviderStateMixin on GetxController @@ -82,6 +82,99 @@ mixin GetSingleTickerProviderStateMixin on GetxController
82 } 82 }
83 } 83 }
84 84
  85 +/// Used like `TickerProviderMixin` but only with Get Controllers.
  86 +/// Simplifies multiple AnimationController creation inside GetxController.
  87 +///
  88 +/// Example:
  89 +///```
  90 +///class SplashController extends GetxController with
  91 +/// GetTickerProviderStateMixin {
  92 +/// AnimationController first_controller;
  93 +/// AnimationController second_controller;
  94 +///
  95 +/// @override
  96 +/// void onInit() {
  97 +/// final duration = const Duration(seconds: 2);
  98 +/// first_controller =
  99 +/// AnimationController.unbounded(duration: duration, vsync: this);
  100 +/// second_controller =
  101 +/// AnimationController.unbounded(duration: duration, vsync: this);
  102 +/// first_controller.repeat();
  103 +/// first_controller.addListener(() =>
  104 +/// print("Animation Controller value: ${first_controller.value}"));
  105 +/// second_controller.addListener(() =>
  106 +/// print("Animation Controller value: ${first_controller.value}"));
  107 +/// }
  108 +/// ...
  109 +/// ```
  110 +mixin GetTickerProviderStateMixin on GetxController implements TickerProvider {
  111 + Set<Ticker>? _tickers;
  112 +
  113 + @override
  114 + Ticker createTicker(TickerCallback onTick) {
  115 + _tickers ??= <_WidgetTicker>{};
  116 + final result = _WidgetTicker(onTick, this, debugLabel: kDebugMode ? 'created by ${describeIdentity(this)}' : null);
  117 + _tickers!.add(result);
  118 + return result;
  119 + }
  120 +
  121 + void _removeTicker(_WidgetTicker ticker) {
  122 + assert(_tickers != null);
  123 + assert(_tickers!.contains(ticker));
  124 + _tickers!.remove(ticker);
  125 + }
  126 +
  127 + void didChangeDependencies(BuildContext context) {
  128 + final muted = !TickerMode.of(context);
  129 + if (_tickers != null) {
  130 + for (final ticker in _tickers!) {
  131 + ticker.muted = muted;
  132 + }
  133 + }
  134 + }
  135 +
  136 + @override
  137 + void onClose() {
  138 + assert(() {
  139 + if (_tickers != null) {
  140 + for (final ticker in _tickers!) {
  141 + if (ticker.isActive) {
  142 + throw FlutterError.fromParts(<DiagnosticsNode>[
  143 + ErrorSummary('$this was disposed with an active Ticker.'),
  144 + ErrorDescription(
  145 + '$runtimeType created a Ticker via its GetTickerProviderStateMixin, but at the time '
  146 + 'dispose() was called on the mixin, that Ticker was still active. All Tickers must '
  147 + 'be disposed before calling super.dispose().',
  148 + ),
  149 + ErrorHint(
  150 + 'Tickers used by AnimationControllers '
  151 + 'should be disposed by calling dispose() on the AnimationController itself. '
  152 + 'Otherwise, the ticker will leak.',
  153 + ),
  154 + ticker.describeForError('The offending ticker was'),
  155 + ]);
  156 + }
  157 + }
  158 + }
  159 + return true;
  160 + }());
  161 + super.onClose();
  162 + }
  163 +
  164 +}
  165 +
  166 +class _WidgetTicker extends Ticker {
  167 + _WidgetTicker(TickerCallback onTick, this._creator, { String? debugLabel }) : super(onTick, debugLabel: debugLabel);
  168 +
  169 + final GetTickerProviderStateMixin _creator;
  170 +
  171 + @override
  172 + void dispose() {
  173 + _creator._removeTicker(this);
  174 + super.dispose();
  175 + }
  176 +}
  177 +
85 @Deprecated('use GetSingleTickerProviderStateMixin') 178 @Deprecated('use GetSingleTickerProviderStateMixin')
86 179
87 /// Used like `SingleTickerProviderMixin` but only with Get Controllers. 180 /// Used like `SingleTickerProviderMixin` but only with Get Controllers.