EffectController constructor

EffectController({
  1. double? duration,
  2. double? speed,
  3. Curve curve = Curves.linear,
  4. double? reverseDuration,
  5. double? reverseSpeed,
  6. Curve? reverseCurve,
  7. bool infinite = false,
  8. bool alternate = false,
  9. int? repeatCount,
  10. double startDelay = 0.0,
  11. double atMaxDuration = 0.0,
  12. double atMinDuration = 0.0,
  13. VoidCallback? onMax,
  14. VoidCallback? onMin,
})

Factory function for producing common EffectControllers.

In the simplest case, when only duration is provided, this will return a LinearEffectController that grows linearly from 0 to 1 over the period of that duration.

More generally, the produced effect controller allows to add a delay before the beginning of the animation, to animate both forward and in reverse, to iterate several times (or infinitely), to apply an arbitrary curve making the effect progression non-linear, etc.

In the most general case, the animation proceeds through the following steps:

  1. wait for startDelay seconds,
  2. repeat the following steps repeatCount times (or infinitely): a. progress from 0 to 1 over the duration seconds, b. wait for atMaxDuration seconds, c. progress from 1 to 0 over the reverseDuration seconds, d. wait for atMinDuration seconds.

Setting parameter alternate to true is another way to create a controller whose reverseDuration is the same as the forward duration.

As an alternative to specifying durations, you can also provide speed and reverseSpeed parameters, but only for effects where the notion of a speed is well-defined (MeasurableEffects).

If the animation is finite and there are no "backward" or "atMin" stages then the animation will complete at progress == 1, otherwise it will complete at progress == 0.

Before atMaxDuration and atMinDuration a callback function can be provided which will be called after the corresponding progress has finished.

Implementation

factory EffectController({
  double? duration,
  double? speed,
  Curve curve = Curves.linear,
  double? reverseDuration,
  double? reverseSpeed,
  Curve? reverseCurve,
  bool infinite = false,
  bool alternate = false,
  int? repeatCount,
  double startDelay = 0.0,
  double atMaxDuration = 0.0,
  double atMinDuration = 0.0,
  VoidCallback? onMax,
  VoidCallback? onMin,
}) {
  assert(
    (duration ?? 1) >= 0,
    'Duration cannot be negative: $duration',
  );
  assert(
    (reverseDuration ?? 1) >= 0,
    'Reverse duration cannot be negative: $reverseDuration',
  );
  assert(
    (duration != null) || (speed != null),
    'Either duration or speed must be specified',
  );
  assert(
    !(duration != null && speed != null),
    'Both duration and speed cannot be specified at the same time',
  );
  assert(
    !(reverseDuration != null && reverseSpeed != null),
    'Both reverseDuration and reverseSpeed cannot be specified at the '
    'same time',
  );
  assert(
    (speed ?? 1) > 0,
    'Speed must be positive: $speed',
  );
  assert(
    (reverseSpeed ?? 1) > 0,
    'Reverse speed must be positive: $reverseSpeed',
  );
  assert(
    !(infinite && repeatCount != null),
    'An infinite effect cannot have a repeat count',
  );
  assert(
    (repeatCount ?? 1) > 0,
    'Repeat count must be positive: $repeatCount',
  );
  assert(
    startDelay >= 0,
    'Start delay cannot be negative: $startDelay',
  );
  assert(
    atMaxDuration >= 0,
    'At-max duration cannot be negative: $atMaxDuration',
  );
  assert(
    atMinDuration >= 0,
    'At-min duration cannot be negative: $atMinDuration',
  );
  final items = <EffectController>[];

  // FORWARD
  final isLinear = curve == Curves.linear;
  if (isLinear) {
    items.add(
      duration != null
          ? LinearEffectController(duration)
          : SpeedEffectController(LinearEffectController(0), speed: speed!),
    );
  } else {
    items.add(
      duration != null
          ? CurvedEffectController(duration, curve)
          : SpeedEffectController(
              CurvedEffectController(1, curve),
              speed: speed!,
            ),
    );
  }

  // ON MAX CALLBACK
  if (onMax != null) {
    items.add(CallbackController(onMax, progress: 1.0));
  }

  // AT-MAX
  if (atMaxDuration != 0) {
    items.add(PauseEffectController(atMaxDuration, progress: 1.0));
  }

  // REVERSE
  final hasReverse =
      alternate || (reverseDuration != null) || (reverseSpeed != null);
  if (hasReverse) {
    final reverseIsLinear =
        reverseCurve == Curves.linear || ((reverseCurve == null) && isLinear);
    final reverseHasDuration = (reverseDuration != null) ||
        (reverseSpeed == null && duration != null);
    if (reverseIsLinear) {
      items.add(
        reverseHasDuration
            ? ReverseLinearEffectController(reverseDuration ?? duration!)
            : SpeedEffectController(
                ReverseLinearEffectController(0),
                speed: reverseSpeed ?? speed!,
              ),
      );
    } else {
      reverseCurve ??= curve.flipped;
      items.add(
        reverseHasDuration
            ? ReverseCurvedEffectController(
                reverseDuration ?? duration!,
                reverseCurve,
              )
            : SpeedEffectController(
                ReverseCurvedEffectController(1, reverseCurve),
                speed: reverseSpeed ?? speed!,
              ),
      );
    }
  }

  // ON MIN CALLBACK
  if (onMin != null) {
    items.add(CallbackController(onMin, progress: 0.0));
  }

  // AT-MIN
  if (atMinDuration != 0) {
    items.add(PauseEffectController(atMinDuration, progress: 0.0));
  }

  assert(items.isNotEmpty);
  var controller =
      items.length == 1 ? items[0] : SequenceEffectController(items);
  if (infinite) {
    controller = InfiniteEffectController(controller);
  }
  if (repeatCount != null && repeatCount != 1) {
    controller = RepeatedEffectController(controller, repeatCount);
  }
  if (startDelay != 0) {
    controller = DelayedEffectController(controller, delay: startDelay);
  }
  return controller;
}