mergeActiveVariants method
Merges all active variants with their nested variants recursively.
Evaluates which variants are active in context and against
namedVariants, then recursively resolves the nested variants inside each
active variant's style.
Active variants apply in two priority groups, lowest first: those that declare no ContextVariant.widgetStateDependencies, then those that do. Within a group, the variant declared last merges last and so wins on the properties they share.
Implementation
@visibleForTesting
Style<S> mergeActiveVariants(
BuildContext context, {
required Set<NamedVariant> namedVariants,
}) {
final variants = $variants;
if (variants == null) return this;
// Partition, don't sort: declaration order inside a group is load-bearing,
// and List.sort is only stable by accident of the insertion sort it falls
// back to below 32 elements.
final lowPriority = <VariantStyle<S>>[];
final highPriority = <VariantStyle<S>>[];
for (final variantAttr in variants) {
final variant = variantAttr.variant;
final isActive = switch (variant) {
ContextVariant() => variant.when(context),
NamedVariant() => namedVariants.contains(variant),
ContextVariantBuilder() => true,
};
if (!isActive) continue;
// Keyed off the declaration, not the class: FocusVisibleVariant is not a
// WidgetStateVariant yet reads WidgetState.focused just the same, and
// NotVariant forwards whatever its inner variant reads.
//
// The getter stays on ContextVariant rather than moving up to Variant
// because ContextVariant is also the only kind widgetStates walks, so a
// dependency declared on any other kind would never get tracking
// installed.
final readsWidgetState =
variant is ContextVariant &&
variant.widgetStateDependencies.isNotEmpty;
(readsWidgetState ? highPriority : lowPriority).add(variantAttr);
}
// Extract the style from each active variant
final stylesToMerge = <(Style<S>, bool)>[]; // (style, isFromStyleVariation)
for (final variantAttr in lowPriority.followedBy(highPriority)) {
final result = switch (variantAttr.variant) {
ContextVariantBuilder variant => (
variant.build(context) as Style<S>,
false,
),
(ContextVariant() || NamedVariant()) => () {
// Check if the value is a StyleVariation
// ignore: avoid-unrelated-type-assertions
if (variantAttr.value is StyleVariation<S>) {
// ignore: avoid-unrelated-type-casts
final styleVariation = variantAttr.value as StyleVariation<S>;
// Only apply if this variant is active
if (namedVariants.contains(styleVariation.variantType)) {
return (
styleVariation.styleBuilder(this, namedVariants, context),
true,
);
}
}
return (variantAttr.value, false);
}(),
};
stylesToMerge.add(result);
}
// Start with current style as base
Style<S> mergedStyle = this;
// Merge each variant style, recursively resolving nested variants
for (final (variantStyle, isFromStyleVariation) in stylesToMerge) {
final fullyResolvedStyle = isFromStyleVariation
// For StyleVariation results, we don't recursively resolve variants
// since StyleVariation.styleBuilder should handle its own variant logic
// and return a final style. This prevents infinite recursion.
? variantStyle
// For regular variants, recursively resolve any nested variants
: variantStyle.mergeActiveVariants(
context,
namedVariants: namedVariants,
);
mergedStyle = _mergeStyles(mergedStyle, fullyResolvedStyle);
}
return mergedStyle;
}