-
Notifications
You must be signed in to change notification settings - Fork 46
Description
Token callable syntax ($token()) silently fails outside of Mix context with confusing error
Summary
Using design tokens with the callable syntax ($spaceLg(), $primary(), etc.) outside of Mix's styling utilities produces confusing Flutter errors instead of helpful error messages. This is an easy mistake to make because the syntax looks like a normal function call returning the expected type.
Problem
The Mistake
It's very easy to accidentally write:
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: EdgeInsets.all($spaceLg()), // ❌ WRONG - looks correct but doesn't work
child: ...
);
}Instead of the correct approaches:
// Option 1: Resolve the token with context
padding: EdgeInsets.all($spaceLg.resolve(context))
// Option 2: Use Mix widgets that understand tokens
Box(
style: BoxStyler().paddingAll($spaceLg()), // ✅ Works - Mix resolves the token
child: ...
)Why This Happens
When you call $spaceLg(), it doesn't return the actual spacing value. Instead, it returns a sentinel value - a negative number used internally by Mix to identify and resolve tokens later:
// From token_refs.dart
extension type const DoubleRef(double _value) implements double {
static DoubleRef token(MixToken<double> token) {
// Generate unique negative nano-value to avoid collisions with real values
final hash = token.hashCode.abs() % 100000;
final ref = DoubleRef(-(0.000001 + hash * 0.000001)); // Returns NEGATIVE value!
_tokenRegistry[ref] = token;
return ref;
}
}The DoubleRef extension type implements double, so it passes type checking. But the actual value is a small negative number like -0.000042.
The Confusing Error
When Flutter tries to use this negative value as padding, you get:
════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#a94d3]:
'package:flutter/src/rendering/shifted_box.dart': Failed assertion: line 117 pos 15: 'padding.isNonNegative': is not true.
Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new?template=02_bug.yml
This error message:
- Points to Flutter's internal code, not the actual problem
- Says "padding.isNonNegative" which is technically true but unhelpful
- Suggests filing a bug with Flutter (!), wasting the developer's time
- Gives zero indication that a Mix token is being misused
Impact
- Easy to make the mistake: The callable syntax
$token()looks exactly like a function returning a value - Confusing error: The Flutter assertion doesn't help identify the root cause
- Time wasted: Developers may spend significant time debugging or even file issues with Flutter
- Silent for some types:
ColorRefand other class-based refs throw more helpful errors, butDoubleRef(used for spacing, doubles) silently returns bad values
Reproduction
import 'package:flutter/material.dart';
import 'package:mix/mix.dart';
const $spaceLg = SpaceToken('space.lg');
class Example extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MixScope(
spaces: {$spaceLg: 24.0},
child: Builder(
builder: (context) {
return SingleChildScrollView(
padding: EdgeInsets.all($spaceLg()), // 💥 Crashes here
child: Text('Hello'),
);
},
),
);
}
}AI Generated Solutions
Option 1: Runtime detection and helpful error (Recommended)
Modify DoubleRef to throw a helpful error when the underlying value is accessed directly. This would require using a class-based approach instead of extension types:
final class DoubleRef implements double {
final MixToken<double> _token;
@override
Never noSuchMethod(Invocation invocation) {
throw UnimplementedError('''
Cannot use token "${_token.name}" directly.
Token references can only be used within Mix styling utilities.
To get the actual value, use one of:
• $token.resolve(context) - resolves using BuildContext
• Inside Mix widgets: BoxStyler().paddingAll($token()) - resolved automatically
''');
}
}Option 2: Compile-time lint rule
Add a lint rule to mix_lint that detects when token callables are used in non-Mix contexts.
Option 3: Documentation and warnings
At minimum, add clear documentation about this pitfall and consider adding a comment in the generated code.
Affected Token Types
SpaceToken→ returnsDoubleRef(negative sentinel)DoubleToken→ returnsDoubleRef(negative sentinel)- Other tokens (
ColorToken,RadiusToken, etc.) return class-based refs that have better error handling throughValueRef.noSuchMethod
Related Files
packages/mix/lib/src/theme/tokens/value_tokens.dart- Token definitionspackages/mix/lib/src/theme/tokens/token_refs.dart- Reference types includingDoubleRefexamples/lib/api/context_variants/design_token.dart:332- Example of the bug
Environment
- Mix version: 2.0.0-rc.0
- Dart SDK: >=3.10.0
- Flutter: >=3.38.1