Skip to content

Token callable syntax ($token()) silently fails outside of Mix context with confusing error #829

@tilucasoli

Description

@tilucasoli

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:

  1. Points to Flutter's internal code, not the actual problem
  2. Says "padding.isNonNegative" which is technically true but unhelpful
  3. Suggests filing a bug with Flutter (!), wasting the developer's time
  4. 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: ColorRef and other class-based refs throw more helpful errors, but DoubleRef (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 → returns DoubleRef (negative sentinel)
  • DoubleToken → returns DoubleRef (negative sentinel)
  • Other tokens (ColorToken, RadiusToken, etc.) return class-based refs that have better error handling through ValueRef.noSuchMethod

Related Files

  • packages/mix/lib/src/theme/tokens/value_tokens.dart - Token definitions
  • packages/mix/lib/src/theme/tokens/token_refs.dart - Reference types including DoubleRef
  • examples/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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions