-
Notifications
You must be signed in to change notification settings - Fork 46
Open
Description
ColorToken cannot chain color operations (withValues, withOpacity)
Description
ColorRef (returned by calling a ColorToken) implements Color but throws UnimplementedError when you try to call color manipulation methods on it.
What throws:
$primary().withValues(alpha: 0.5) // ❌ UnimplementedError at runtime
$primary().withOpacity(0.5) // ❌ UnimplementedError at runtimeSteps to Reproduce
const $primary = ColorToken('brand.primary');
// In a widget build method:
final style = BoxStyler()
.color($primary()) // ✅ Works
.borderAll(color: $primary().withValues(alpha: 0.5)); // ❌ Throws at runtime!Error message:
UnimplementedError: Cannot access 'withValues' on a Color token reference.
This is a context-dependent Color token that needs to be resolved through BuildContext before use.
Token references can only be passed directly to Mix styling utilities (e.g., $box.color).
To use as an actual Color value:
- Pass it to Mix utilities: $box.color.token(myColorToken)
- Or resolve it first: myColorToken.resolve(context)
Current Workaround
Resolve the token first, then apply operations:
@override
Widget build(BuildContext context) {
// Resolve first, then apply operations
final primaryColor = $primary.resolve(context);
final style = BoxStyler()
.color(primaryColor)
.borderAll(color: primaryColor.withValues(alpha: 0.5));
return Box(style: style, child: child);
}This works but:
- Loses the ergonomic token syntax
- Requires BuildContext at style definition time
- Can't define styles outside of build methods
Expected Behavior
Color tokens should support chaining color operations that get applied when the token is resolved:
// Ideal API - operations stored as directives, applied at resolution
$primary.withValues(alpha: 0.5) // Returns a modified token ref
$primary.withOpacity(0.5) // Returns a modified token ref
// Usage in styles
final style = BoxStyler()
.color($primary())
.borderAll(color: $primary.withValues(alpha: 0.5)); // Should work!Root Cause
In packages/mix/lib/src/theme/tokens/token_refs.dart:
final class ColorRef extends Prop<Color> with ValueRef<Color> implements Color {
ColorRef(super.prop) : super.fromProp();
}
mixin ValueRef<T> {
@override
Never noSuchMethod(Invocation invocation) {
throw UnimplementedError(_buildTokenReferenceError(invocation.memberName));
}
}ColorRef implements Color (so it can be passed where Color is expected) but delegates all unimplemented methods to noSuchMethod which throws.
Proposed Solutions
Option 1: Add directive methods to ColorToken
Add methods directly to ColorToken that return a new token reference with directives:
class ColorToken extends MixToken<Color> {
const ColorToken(super.name);
@override
ColorRef call() => ColorRef(Prop.token(this));
/// Returns a token ref with alpha applied when resolved
ColorRef withValues({double? alpha, double? red, double? green, double? blue}) => ColorRef(
Prop.directives([WithValuesColorDirective(alpha: alpha, red: red, green: green, blue: blue)], baseToken: this),
);
/// Returns a token ref with opacity applied when resolved
ColorRef withOpacity(double opacity) => ColorRef(
Prop.directives([OpacityColorDirective(opacity)], baseToken: this),
);
}Option 2: Add methods to ColorRef
Make ColorRef return new ColorRef instances with accumulated directives:
final class ColorRef extends Prop<Color> with ValueRef<Color> implements Color {
ColorRef(super.prop) : super.fromProp();
@override
ColorRef withValues({double? alpha, double? red, double? green, double? blue, ColorSpace? colorSpace}) => ColorRef(
appendDirective(WithValuesColorDirective(alpha: alpha, red: red, green: green, blue: blue, colorSpace: colorSpace)),
);
@override
ColorRef withOpacity(double opacity) => ColorRef(
appendDirective(OpacityColorDirective(opacity)),
);
}Related Files
packages/mix/lib/src/theme/tokens/value_tokens.dart- ColorToken definitionpackages/mix/lib/src/theme/tokens/token_refs.dart- ColorRef with noSuchMethodpackages/mix/lib/src/core/directive.dart- Color directives (WithValuesColorDirective, OpacityColorDirective)packages/mix/lib/src/properties/painting/color_util.dart- ColorDirectiveMixin with existing directive supportexamples/lib/api/context_variants/colortoken_operations_issue.dart- Reproduction example
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels