Support value-semantic custom ContextVariant types
- Dominant language
- Dart
- Stars
- 800
- Forks
- 49
- Avg merge
- 3h 39m
- Merged PRs (30d)
- 22
Description
### Use case
`ContextVariant` accepts an arbitrary `bool Function(BuildContext)` and therefore retains identity equality. When a pure style factory constructs the same logical custom variant on each invocation, the resulting styles compare unequal and their variants do not share merge identity.
This surfaced in [`conceptadev/remix#121`](https://github.com/conceptadev/remix/issues/121): `CheckboxStyler.onIndeterminate` created the same logical variant on every call, but each instance contained a new closure. [`conceptadev/remix#124`](https://github.com/conceptadev/remix/pull/124) works around it by reusing a single variant instance.
Downstream custom variants currently have two choices:
- retain a shared singleton; or
- subclass `ContextVariant` and repeat `operator ==`/`hashCode` boilerplate already implemented independently by Mix's built-in variants.
Using `ContextVariant.key` as equality is unsafe because Mix documents it as a human-readable diagnostic label and deliberately keeps it out of semantic merge identity.
Relevant implementation:
- [`ContextVariant` and its diagnostic key](https://github.com/conceptadev/mix/blob/ab7a9ab8507a49c530d0475d1a6e38f26b158d1e/packages/mix/lib/src/variants/variant.dart#L21-L55)
- [Context-variant merge identity](https://github.com/conceptadev/mix/blob/ab7a9ab8507a49c530d0475d1a6e38f26b158d1e/packages/mix/lib/src/core/style.dart#L276-L285)
### Proposal
Provide an opt-in API for custom context variants with explicit value semantics while preserving the identity behavior of the existing `ContextVariant` constructor.
One possible API is an abstract base class:
```dart
abstract base class ValueContextVariant extends ContextVariant {
const ValueContextVariant(super.key, super.shouldApply);
List get props => const [];
@override
bool operator ==(Object other) =>
identical(this, other) ||
other.runtimeType == runtimeType &&
other is ValueContextVariant &&
propsEquals(props, other.props);
@override
int get hashCode => propsHash(runtimeType, props);
}
```
Proposed downstream usage:
```dart
final class IndeterminateVariant extends ValueContextVariant {
const IndeterminateVariant()
: super('on_indeterminate', _shouldApply);
static bool _shouldApply(BuildContext context) {
return CheckboxState.maybeOf(context)?.isChecked == null;
}
}
```
The exact API could instead be a constructor/factory accepting an explicit semantic identity. The important requirements are:
- existing directly constructed `ContextVariant`s retain identity semantics;
- custom variants can opt into consistent equality and hash codes;
- semantic identity is separate from the diagnostic `key`;
- different variant types or value properties cannot collide accidentally;
- `VariantStyle` equality and merge behavior honor the custom value semantics;
- tests cover equal instances, unequal properties, identical diagnostic keys with distinct identities, and unchanged built-in variant behavior.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with ContextVariant and its diagnostic key in packages/mix/lib/src/variants/variant.dart, then trace context-variant merge identity in packages/mix/lib/src/core/style.dart. Compare the existing built-in variant equality behavior and decide on an opt-in value-semantics API without changing direct ContextVariant construction. Done means equal custom instances merge and compare equally, differing types or properties do not collide, and the requested equality and unchanged built-in behavior are covered by tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart, flutter
- Domain
- design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100