AnimationTimeline.converge constructor
AnimationTimeline.converge({
- required List<
AnimationController> controllers, - Duration gap = Duration.zero,
- Duration crestHold = Duration.zero,
- Duration returnGap = Duration.zero,
- Duration rest = Duration.zero,
- bool repeat = false,
- Object? id,
- String labelBuilder()?,
- void onStepStart(
- int index,
- AnimationTimelineStep step,
- TimelineDirection direction
- void onStepComplete(
- int index,
- AnimationTimelineStep step,
- TimelineDirection direction
Builds an edge-origin convergence across multiple controllers.
Controllers are animated inward from the outer edges toward the center. The return sweep reverses that resolved order back toward the edges.
This is useful for bracket-like emphasis, collapsing rails, and edge-first convergence patterns that are the spatial counterpart to ripple's center-out motion.
Implementation
factory AnimationTimeline.converge({
required List<AnimationController> controllers,
Duration gap = Duration.zero,
Duration crestHold = Duration.zero,
Duration returnGap = Duration.zero,
Duration rest = Duration.zero,
bool repeat = false,
Object? id,
String Function(int index, String phase)? labelBuilder,
void Function(
int index,
AnimationTimelineStep step,
TimelineDirection direction,
)?
onStepStart,
void Function(
int index,
AnimationTimelineStep step,
TimelineDirection direction,
)?
onStepComplete,
}) {
if (controllers.isEmpty) {
throw ArgumentError.value(
controllers,
'controllers',
'must not be empty',
);
}
final order = <int>[];
var left = 0;
var right = controllers.length - 1;
while (left <= right) {
if (left == right) {
order.add(left);
} else {
order.add(left);
order.add(right);
}
left++;
right--;
}
final steps = <AnimationTimelineStep>[];
for (var position = 0; position < order.length; position++) {
final index = order[position];
if (position > 0 && gap > Duration.zero) {
steps.add(
AnimationTimelineStep.delay(
gap,
label: labelBuilder?.call(index, 'gap') ?? 'converge-gap-$position',
),
);
}
steps.add(
AnimationTimelineStep.forward(
controllers[index],
label: labelBuilder?.call(index, 'in') ?? 'converge-in-$index',
),
);
}
if (crestHold > Duration.zero) {
final centerIndex = order.last;
steps.add(
AnimationTimelineStep.delay(
crestHold,
label:
labelBuilder?.call(centerIndex, 'crest-hold') ??
'converge-crest-hold',
),
);
}
for (var position = order.length - 1; position >= 0; position--) {
final index = order[position];
if (position < order.length - 1 && returnGap > Duration.zero) {
steps.add(
AnimationTimelineStep.delay(
returnGap,
label:
labelBuilder?.call(index, 'return-gap') ??
'converge-return-gap-$position',
),
);
}
steps.add(
AnimationTimelineStep.reverse(
controllers[index],
label: labelBuilder?.call(index, 'out') ?? 'converge-out-$index',
),
);
}
if (rest > Duration.zero) {
steps.add(
AnimationTimelineStep.delay(
rest,
label: labelBuilder?.call(order.first, 'rest') ?? 'converge-rest',
),
);
}
return AnimationTimeline(
steps: steps,
repeat: repeat,
id: id,
onStepStart: onStepStart,
onStepComplete: onStepComplete,
);
}