diff --git a/examples/todo_list/lib/main.dart b/examples/todo_list/lib/main.dart index 0a4e4df2e..36639870f 100644 --- a/examples/todo_list/lib/main.dart +++ b/examples/todo_list/lib/main.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:mix/mix.dart'; -import 'package:todo_list/pages/todo_list_page.dart'; +import 'pages/todo_list_page.dart'; import 'style/design_tokens.dart'; void main() { diff --git a/examples/todo_list/lib/style/components/checkbox.dart b/examples/todo_list/lib/style/components/checkbox.dart index d3c47fc54..aae58f560 100644 --- a/examples/todo_list/lib/style/components/checkbox.dart +++ b/examples/todo_list/lib/style/components/checkbox.dart @@ -30,7 +30,15 @@ class TodoCheckbox extends StatelessWidget { $box.borderRadius(3), scaleEffect(), outlinePattern(), + $icon.size(16), + $icon.color.ref($token.color.surface), + $icon.modifiers.opacity(0), + $icon.modifiers.padding.top(5), + $icon.modifiers.scale(0.5), _CheckboxVariant.checked( + $icon.modifiers.padding.top(0), + $icon.modifiers.scale(2), + $icon.modifiers.opacity(1), $box.color.ref($token.color.primary), $box.border.color.ref($token.color.primary), ), @@ -41,24 +49,8 @@ class TodoCheckbox extends StatelessWidget { .animate( duration: const Duration(milliseconds: 150), ), - child: StyledIcon( + child: const StyledIcon( Icons.check, - style: Style( - $icon.weight(16), - $icon.color.ref($token.color.surface), - $with.opacity(0), - $with.padding.top(5), - _CheckboxVariant.checked( - $with.padding.top(0), - $with.opacity(1), - ), - ) - .applyVariant( - value ? _CheckboxVariant.checked : _CheckboxVariant.unchecked, - ) - .animate( - duration: const Duration(milliseconds: 300), - ), ), ); } diff --git a/packages/mix/lib/src/attributes/attributes.dart b/packages/mix/lib/src/attributes/attributes.dart index 1b754a305..b0ce4de3f 100644 --- a/packages/mix/lib/src/attributes/attributes.dart +++ b/packages/mix/lib/src/attributes/attributes.dart @@ -24,6 +24,9 @@ export 'enum/enum_util.dart'; export 'gap/gap_util.dart'; export 'gap/spacing_side_dto.dart'; export 'gradient/gradient_dto.dart'; +export 'modifiers/widget_modifiers_data.dart'; +export 'modifiers/widget_modifiers_data_dto.dart'; +export 'modifiers/widget_modifiers_util.dart'; export 'nested_style/nested_style_attribute.dart'; export 'nested_style/nested_style_util.dart'; export 'scalars/scalar_util.dart'; diff --git a/packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart b/packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart deleted file mode 100644 index ff24be7ef..000000000 --- a/packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart +++ /dev/null @@ -1,29 +0,0 @@ -import '../../core/core.dart'; -import 'modifiers_data.dart'; - -class ModifiersDataDto extends Dto { - final List value; - - const ModifiersDataDto(this.value); - - @override - ModifiersDataDto merge(ModifiersDataDto? other) { - if (other == null) return this; - final thisMap = AttributeMap(value); - final otherMap = AttributeMap(other.value); - final mergedMap = thisMap.merge(otherMap).values; - - return ModifiersDataDto(mergedMap); - } - - @override - ModifiersData resolve(MixData mix) { - return ModifiersData(value.map((e) => e.resolve(mix)).toList()); - } - - @override - ModifiersData get defaultValue => const ModifiersData([]); - - @override - List get props => [value]; -} diff --git a/packages/mix/lib/src/attributes/modifiers/modifiers_data.dart b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_data.dart similarity index 62% rename from packages/mix/lib/src/attributes/modifiers/modifiers_data.dart rename to packages/mix/lib/src/attributes/modifiers/widget_modifiers_data.dart index d151ce7d6..deb4f86fe 100644 --- a/packages/mix/lib/src/attributes/modifiers/modifiers_data.dart +++ b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_data.dart @@ -1,7 +1,7 @@ import '../../core/modifier.dart'; -class ModifiersData { +class WidgetModifiersData { // ignore: avoid-dynamic final List> value; - const ModifiersData(this.value); + const WidgetModifiersData(this.value); } diff --git a/packages/mix/lib/src/attributes/modifiers/widget_modifiers_data_dto.dart b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_data_dto.dart new file mode 100644 index 000000000..628411454 --- /dev/null +++ b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_data_dto.dart @@ -0,0 +1,29 @@ +import '../../core/core.dart'; +import 'widget_modifiers_data.dart'; + +class WidgetModifiersDataDto extends Dto { + final List value; + + const WidgetModifiersDataDto(this.value); + + @override + WidgetModifiersDataDto merge(WidgetModifiersDataDto? other) { + if (other == null) return this; + final thisMap = AttributeMap(value); + final otherMap = AttributeMap(other.value); + final mergedMap = thisMap.merge(otherMap).values; + + return WidgetModifiersDataDto(mergedMap); + } + + @override + WidgetModifiersData resolve(MixData mix) { + return WidgetModifiersData(value.map((e) => e.resolve(mix)).toList()); + } + + @override + WidgetModifiersData get defaultValue => const WidgetModifiersData([]); + + @override + List get props => [value]; +} diff --git a/packages/mix/lib/src/attributes/modifiers/modifiers_util.dart b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart similarity index 79% rename from packages/mix/lib/src/attributes/modifiers/modifiers_util.dart rename to packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart index 83902103e..f61c00916 100644 --- a/packages/mix/lib/src/attributes/modifiers/modifiers_util.dart +++ b/packages/mix/lib/src/attributes/modifiers/widget_modifiers_util.dart @@ -1,11 +1,12 @@ import '../../core/core.dart'; import '../../modifiers/modifiers.dart'; import '../spacing/spacing_util.dart'; -import 'modifiers_data_dto.dart'; +import 'widget_modifiers_data_dto.dart'; -final class ModifiersDataUtility - extends MixUtility { - late final add = WithModifierUtility((v) => builder(ModifiersDataDto([v]))); +final class ModifierUtility + extends MixUtility { + late final add = + WithModifierUtility((v) => builder(WidgetModifiersDataDto([v]))); late final intrinsicWidth = IntrinsicWidthWidgetUtility(only); late final intrinsicHeight = IntrinsicHeightWidgetUtility(only); @@ -30,9 +31,9 @@ final class ModifiersDataUtility late final sizedBox = SizedBoxModifierUtility(only); late final padding = SpacingUtility(PaddingModifierUtility(only)); - ModifiersDataUtility(super.builder); + ModifierUtility(super.builder); T only(WidgetModifierAttribute attribute) { - return builder(ModifiersDataDto([attribute])); + return builder(WidgetModifiersDataDto([attribute])); } } diff --git a/packages/mix/lib/src/core/spec.dart b/packages/mix/lib/src/core/spec.dart index 9e39863a4..94524d406 100644 --- a/packages/mix/lib/src/core/spec.dart +++ b/packages/mix/lib/src/core/spec.dart @@ -2,8 +2,8 @@ import 'package:flutter/foundation.dart'; import '../attributes/animated/animated_data.dart'; import '../attributes/animated/animated_data_dto.dart'; -import '../attributes/modifiers/modifiers_data.dart'; -import '../attributes/modifiers/modifiers_data_dto.dart'; +import '../attributes/modifiers/widget_modifiers_data.dart'; +import '../attributes/modifiers/widget_modifiers_data_dto.dart'; import '../internal/compare_mixin.dart'; import 'attribute.dart'; import 'factory/mix_data.dart'; @@ -12,7 +12,7 @@ import 'utility.dart'; @immutable abstract class Spec> with EqualityMixin { final AnimatedData? animated; - final ModifiersData? modifiers; + final WidgetModifiersData? modifiers; const Spec({this.animated, this.modifiers}); @@ -34,7 +34,7 @@ abstract class Spec> with EqualityMixin { /// The [Self] type represents the concrete implementation of the attribute, while the [Value] type represents the resolvable value. abstract base class SpecAttribute extends StyledAttribute { final AnimatedDataDto? animated; - final ModifiersDataDto? modifiers; + final WidgetModifiersDataDto? modifiers; const SpecAttribute({this.animated, this.modifiers}); diff --git a/packages/mix/lib/src/modifiers/render_widget_modifier.dart b/packages/mix/lib/src/modifiers/render_widget_modifier.dart index d4cdbc527..a90637039 100644 --- a/packages/mix/lib/src/modifiers/render_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/render_widget_modifier.dart @@ -70,6 +70,23 @@ const _defaultOrder = [ OpacityModifierAttribute, ]; +const _defaultOrderSpecs = [ + VisibilityModifierSpec, + SizedBoxModifierSpec, + FractionallySizedBoxModifierSpec, + AlignModifierSpec, + IntrinsicHeightModifierSpec, + IntrinsicWidthModifierSpec, + AspectRatioModifierSpec, + TransformModifierSpec, + ClipOvalModifierSpec, + ClipRRectModifierSpec, + ClipPathModifierSpec, + ClipTriangleModifierSpec, + ClipRectModifierSpec, + OpacityModifierSpec, +]; + class RenderModifiers extends StatelessWidget { const RenderModifiers({ required this.child, @@ -152,6 +169,31 @@ Set resolveModifierSpecs( return orderModifierSpecs(orderOfModifiers, mix, modifiers); } +Set> orderSpecs( + List orderOfModifiers, [ + Set> modifiers = const {}, +]) { + final listOfModifiers = ({ + // Prioritize the order of modifiers provided by the user. + ...orderOfModifiers, + // Add the default order of modifiers. + ..._defaultOrderSpecs, + // Add any remaining modifiers that were not included in the order. + ...modifiers.map((e) => e.type), + }).toList(); + + final specs = >{}; + + for (final modifierType in listOfModifiers) { + // Resolve the modifier and add it to the list of specs. + final modifier = modifiers.where((e) => e.type == modifierType).firstOrNull; + if (modifier == null) continue; + specs.add(modifier as WidgetModifierSpec>); + } + + return specs; +} + Set orderModifierSpecs( List orderOfModifiers, MixData mix, @@ -182,14 +224,12 @@ Set orderModifierSpecs( class RenderInlineModifiers extends StatelessWidget { const RenderInlineModifiers({ - required this.mix, required this.orderOfModifiers, required this.child, required this.spec, super.key, }); - final MixData mix; final Widget child; final List orderOfModifiers; final Spec spec; @@ -198,13 +238,19 @@ class RenderInlineModifiers extends StatelessWidget { Widget build(BuildContext context) { return spec.isAnimated ? RenderAnimatedModifiers( - modifiers: spec.modifiers?.value.toSet() ?? {}, + modifiers: orderSpecs( + orderOfModifiers, + spec.modifiers?.value.toSet() ?? {}, + ), duration: spec.animated!.duration, curve: spec.animated!.curve, child: child, ) : RenderModifiers( - modifiers: spec.modifiers?.value.toSet() ?? {}, + modifiers: orderSpecs( + orderOfModifiers, + spec.modifiers?.value.toSet() ?? {}, + ), child: child, ); } diff --git a/packages/mix/lib/src/specs/box/box_spec.dart b/packages/mix/lib/src/specs/box/box_spec.dart index 115158fca..34d50703e 100644 --- a/packages/mix/lib/src/specs/box/box_spec.dart +++ b/packages/mix/lib/src/specs/box/box_spec.dart @@ -3,10 +3,6 @@ import 'package:flutter/widgets.dart'; import 'package:mix/mix.dart'; import 'package:mix_annotations/mix_annotations.dart'; -import '../../attributes/modifiers/modifiers_data.dart'; -import '../../attributes/modifiers/modifiers_data_dto.dart'; -import '../../attributes/modifiers/modifiers_util.dart'; - part 'box_spec.g.dart'; const _constraints = MixableUtility( @@ -104,7 +100,7 @@ final class BoxSpec extends Spec with _$BoxSpec { super.animated, }); - Widget call({Widget? child}) { + Widget call({Widget? child, List orderOfModifiers = const []}) { return isAnimated ? AnimatedBoxSpecWidget( spec: this, @@ -112,6 +108,10 @@ final class BoxSpec extends Spec with _$BoxSpec { curve: animated!.curve, child: child, ) - : BoxSpecWidget(spec: this, child: child); + : BoxSpecWidget( + spec: this, + orderOfModifiers: orderOfModifiers, + child: child, + ); } } diff --git a/packages/mix/lib/src/specs/box/box_spec.g.dart b/packages/mix/lib/src/specs/box/box_spec.g.dart index 94ad86b3a..7735d09f1 100644 --- a/packages/mix/lib/src/specs/box/box_spec.g.dart +++ b/packages/mix/lib/src/specs/box/box_spec.g.dart @@ -43,7 +43,7 @@ base mixin _$BoxSpec on Spec { Clip? clipBehavior, double? width, double? height, - ModifiersData? modifiers, + WidgetModifiersData? modifiers, AnimatedData? animated, }) { return BoxSpec( @@ -347,7 +347,7 @@ base class BoxSpecUtility late final height = DoubleUtility((v) => only(height: v)); /// Utility for defining [BoxSpecAttribute.modifiers] - late final modifiers = ModifiersDataUtility((v) => only(modifiers: v)); + late final modifiers = ModifierUtility((v) => only(modifiers: v)); /// Utility for defining [BoxSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); @@ -370,7 +370,7 @@ base class BoxSpecUtility Clip? clipBehavior, double? width, double? height, - ModifiersDataDto? modifiers, + WidgetModifiersDataDto? modifiers, AnimatedDataDto? animated, }) { return builder(BoxSpecAttribute( diff --git a/packages/mix/lib/src/specs/box/box_widget.dart b/packages/mix/lib/src/specs/box/box_widget.dart index dfa5787e7..4344276cf 100644 --- a/packages/mix/lib/src/specs/box/box_widget.dart +++ b/packages/mix/lib/src/specs/box/box_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/widgets.dart'; import '../../core/factory/mix_provider.dart'; import '../../core/styled_widget.dart'; +import '../../modifiers/render_widget_modifier.dart'; import 'box_spec.dart'; /// A [Container] equivalent widget for applying styles using Mix. @@ -53,39 +54,42 @@ class Box extends StyledWidget { return withMix(context, (context) { final spec = BoxSpec.of(context); - return spec.isAnimated - ? AnimatedBoxSpecWidget( - spec: spec, - duration: spec.animated!.duration, - curve: spec.animated!.curve, - child: child, - ) - : BoxSpecWidget(spec: spec, child: child); + return spec(child: child); }); } } class BoxSpecWidget extends StatelessWidget { - const BoxSpecWidget({required this.spec, super.key, this.child}); + const BoxSpecWidget({ + required this.spec, + super.key, + this.child, + this.orderOfModifiers = const [], + }); final Widget? child; final BoxSpec? spec; + final List orderOfModifiers; @override Widget build(BuildContext context) { - return Container( - alignment: spec?.alignment, - padding: spec?.padding, - decoration: spec?.decoration, - foregroundDecoration: spec?.foregroundDecoration, - width: spec?.width, - height: spec?.height, - constraints: spec?.constraints, - margin: spec?.margin, - transform: spec?.transform, - transformAlignment: spec?.transformAlignment, - clipBehavior: spec?.clipBehavior ?? Clip.none, - child: child, + return RenderInlineModifiers( + orderOfModifiers: orderOfModifiers, + spec: spec ?? const BoxSpec(), + child: Container( + alignment: spec?.alignment, + padding: spec?.padding, + decoration: spec?.decoration, + foregroundDecoration: spec?.foregroundDecoration, + width: spec?.width, + height: spec?.height, + constraints: spec?.constraints, + margin: spec?.margin, + transform: spec?.transform, + transformAlignment: spec?.transformAlignment, + clipBehavior: spec?.clipBehavior ?? Clip.none, + child: child, + ), ); } } @@ -98,10 +102,12 @@ class AnimatedBoxSpecWidget extends ImplicitlyAnimatedWidget { required super.duration, super.curve = Curves.linear, super.onEnd, + this.orderOfModifiers = const [], }); final Widget? child; final BoxSpec spec; + final List orderOfModifiers; @override AnimatedWidgetBaseState createState() => diff --git a/packages/mix/lib/src/specs/flex/flex_spec.dart b/packages/mix/lib/src/specs/flex/flex_spec.dart index 6a4e2bc02..74377de13 100644 --- a/packages/mix/lib/src/specs/flex/flex_spec.dart +++ b/packages/mix/lib/src/specs/flex/flex_spec.dart @@ -46,6 +46,7 @@ final class FlexSpec extends Spec with _$FlexSpec { this.clipBehavior, this.gap, super.animated, + super.modifiers, }); Widget call({List children = const [], required Axis direction}) { diff --git a/packages/mix/lib/src/specs/flex/flex_spec.g.dart b/packages/mix/lib/src/specs/flex/flex_spec.g.dart index 78ed6bb1b..9e39821b0 100644 --- a/packages/mix/lib/src/specs/flex/flex_spec.g.dart +++ b/packages/mix/lib/src/specs/flex/flex_spec.g.dart @@ -43,6 +43,7 @@ base mixin _$FlexSpec on Spec { Clip? clipBehavior, double? gap, AnimatedData? animated, + WidgetModifiersData? modifiers, }) { return FlexSpec( crossAxisAlignment: crossAxisAlignment ?? _$this.crossAxisAlignment, @@ -55,6 +56,7 @@ base mixin _$FlexSpec on Spec { clipBehavior: clipBehavior ?? _$this.clipBehavior, gap: gap ?? _$this.gap, animated: animated ?? _$this.animated, + modifiers: modifiers ?? _$this.modifiers, ); } @@ -71,7 +73,7 @@ base mixin _$FlexSpec on Spec { /// /// - [MixHelpers.lerpDouble] for [gap]. - /// For [crossAxisAlignment] and [mainAxisAlignment] and [mainAxisSize] and [verticalDirection] and [direction] and [textDirection] and [textBaseline] and [clipBehavior] and [animated], the interpolation is performed using a step function. + /// For [crossAxisAlignment] and [mainAxisAlignment] and [mainAxisSize] and [verticalDirection] and [direction] and [textDirection] and [textBaseline] and [clipBehavior] and [animated] and [modifiers], the interpolation is performed using a step function. /// If [t] is less than 0.5, the value from the current [FlexSpec] is used. Otherwise, the value /// from the [other] [FlexSpec] is used. /// @@ -95,6 +97,7 @@ base mixin _$FlexSpec on Spec { clipBehavior: t < 0.5 ? _$this.clipBehavior : other.clipBehavior, gap: MixHelpers.lerpDouble(_$this.gap, other.gap, t), animated: t < 0.5 ? _$this.animated : other.animated, + modifiers: t < 0.5 ? _$this.modifiers : other.modifiers, ); } @@ -114,6 +117,7 @@ base mixin _$FlexSpec on Spec { _$this.clipBehavior, _$this.gap, _$this.animated, + _$this.modifiers, ]; FlexSpec get _$this => this as FlexSpec; @@ -148,6 +152,7 @@ final class FlexSpecAttribute extends SpecAttribute { this.clipBehavior, this.gap, super.animated, + super.modifiers, }); /// Resolves to [FlexSpec] using the provided [MixData]. @@ -171,6 +176,7 @@ final class FlexSpecAttribute extends SpecAttribute { clipBehavior: clipBehavior, gap: gap?.resolve(mix), animated: animated?.resolve(mix) ?? mix.animation, + modifiers: modifiers?.resolve(mix), ); } @@ -197,6 +203,7 @@ final class FlexSpecAttribute extends SpecAttribute { clipBehavior: other.clipBehavior ?? clipBehavior, gap: gap?.merge(other.gap) ?? other.gap, animated: animated?.merge(other.animated) ?? other.animated, + modifiers: modifiers?.merge(other.modifiers) ?? other.modifiers, ); } @@ -216,6 +223,7 @@ final class FlexSpecAttribute extends SpecAttribute { clipBehavior, gap, animated, + modifiers, ]; } @@ -265,6 +273,9 @@ base class FlexSpecUtility /// Utility for defining [FlexSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); + /// Utility for defining [FlexSpecAttribute.modifiers] + late final modifiers = ModifierUtility((v) => only(modifiers: v)); + FlexSpecUtility(super.builder); static final self = FlexSpecUtility((v) => v); @@ -282,6 +293,7 @@ base class FlexSpecUtility Clip? clipBehavior, SpacingSideDto? gap, AnimatedDataDto? animated, + WidgetModifiersDataDto? modifiers, }) { return builder(FlexSpecAttribute( crossAxisAlignment: crossAxisAlignment, @@ -294,6 +306,7 @@ base class FlexSpecUtility clipBehavior: clipBehavior, gap: gap, animated: animated, + modifiers: modifiers, )); } } diff --git a/packages/mix/lib/src/specs/flex/flex_widget.dart b/packages/mix/lib/src/specs/flex/flex_widget.dart index 2c21f38c1..99c9a2ded 100644 --- a/packages/mix/lib/src/specs/flex/flex_widget.dart +++ b/packages/mix/lib/src/specs/flex/flex_widget.dart @@ -4,6 +4,7 @@ import 'package:flutter/widgets.dart'; import '../../core/factory/style_mix.dart'; import '../../core/styled_widget.dart'; +import '../../modifiers/render_widget_modifier.dart'; import '../box/box_spec.dart'; import '../box/box_widget.dart'; import 'flex_spec.dart'; @@ -47,6 +48,7 @@ class StyledFlex extends StyledWidget { return FlexSpecWidget( spec: spec, direction: direction, + orderOfModifiers: orderOfModifiers, children: children, ); }); @@ -59,11 +61,13 @@ class FlexSpecWidget extends StatelessWidget { this.spec, required this.children, required this.direction, + this.orderOfModifiers = const [], }); final List children; final Axis direction; final FlexSpec? spec; + final List orderOfModifiers; List _buildChildren(double? gap) { if (gap == null) return children; @@ -80,8 +84,7 @@ class FlexSpecWidget extends StatelessWidget { @override Widget build(BuildContext context) { final gap = spec?.gap; - - return Flex( + final flexWidget = Flex( direction: direction, mainAxisAlignment: spec?.mainAxisAlignment ?? _defaultFlex.mainAxisAlignment, @@ -92,6 +95,14 @@ class FlexSpecWidget extends StatelessWidget { spec?.verticalDirection ?? _defaultFlex.verticalDirection, children: _buildChildren(gap), ); + + return spec == null + ? flexWidget + : RenderInlineModifiers( + orderOfModifiers: orderOfModifiers, + spec: spec!, + child: flexWidget, + ); } } diff --git a/packages/mix/lib/src/specs/icon/icon_spec.dart b/packages/mix/lib/src/specs/icon/icon_spec.dart index 745d1f2d1..77fe11d92 100644 --- a/packages/mix/lib/src/specs/icon/icon_spec.dart +++ b/packages/mix/lib/src/specs/icon/icon_spec.dart @@ -34,6 +34,7 @@ final class IconSpec extends Spec with _$IconSpec { this.applyTextScaling, this.fill, super.animated, + super.modifiers, }); Widget call(IconData? icon, {String? semanticLabel}) { diff --git a/packages/mix/lib/src/specs/icon/icon_spec.g.dart b/packages/mix/lib/src/specs/icon/icon_spec.g.dart index d95b3f931..822087a36 100644 --- a/packages/mix/lib/src/specs/icon/icon_spec.g.dart +++ b/packages/mix/lib/src/specs/icon/icon_spec.g.dart @@ -43,6 +43,7 @@ base mixin _$IconSpec on Spec { bool? applyTextScaling, double? fill, AnimatedData? animated, + WidgetModifiersData? modifiers, }) { return IconSpec( color: color ?? _$this.color, @@ -55,6 +56,7 @@ base mixin _$IconSpec on Spec { applyTextScaling: applyTextScaling ?? _$this.applyTextScaling, fill: fill ?? _$this.fill, animated: animated ?? _$this.animated, + modifiers: modifiers ?? _$this.modifiers, ); } @@ -72,7 +74,7 @@ base mixin _$IconSpec on Spec { /// - [Color.lerp] for [color]. /// - [MixHelpers.lerpDouble] for [size] and [weight] and [grade] and [opticalSize] and [fill]. - /// For [shadows] and [textDirection] and [applyTextScaling] and [animated], the interpolation is performed using a step function. + /// For [shadows] and [textDirection] and [applyTextScaling] and [animated] and [modifiers], the interpolation is performed using a step function. /// If [t] is less than 0.5, the value from the current [IconSpec] is used. Otherwise, the value /// from the [other] [IconSpec] is used. /// @@ -95,6 +97,7 @@ base mixin _$IconSpec on Spec { t < 0.5 ? _$this.applyTextScaling : other.applyTextScaling, fill: MixHelpers.lerpDouble(_$this.fill, other.fill, t), animated: t < 0.5 ? _$this.animated : other.animated, + modifiers: t < 0.5 ? _$this.modifiers : other.modifiers, ); } @@ -114,6 +117,7 @@ base mixin _$IconSpec on Spec { _$this.applyTextScaling, _$this.fill, _$this.animated, + _$this.modifiers, ]; IconSpec get _$this => this as IconSpec; @@ -148,6 +152,7 @@ final class IconSpecAttribute extends SpecAttribute { this.applyTextScaling, this.fill, super.animated, + super.modifiers, }); /// Resolves to [IconSpec] using the provided [MixData]. @@ -171,6 +176,7 @@ final class IconSpecAttribute extends SpecAttribute { applyTextScaling: applyTextScaling, fill: fill, animated: animated?.resolve(mix) ?? mix.animation, + modifiers: modifiers?.resolve(mix), ); } @@ -197,6 +203,7 @@ final class IconSpecAttribute extends SpecAttribute { applyTextScaling: other.applyTextScaling ?? applyTextScaling, fill: other.fill ?? fill, animated: animated?.merge(other.animated) ?? other.animated, + modifiers: modifiers?.merge(other.modifiers) ?? other.modifiers, ); } @@ -216,6 +223,7 @@ final class IconSpecAttribute extends SpecAttribute { applyTextScaling, fill, animated, + modifiers, ]; } @@ -256,6 +264,9 @@ base class IconSpecUtility /// Utility for defining [IconSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); + /// Utility for defining [IconSpecAttribute.modifiers] + late final modifiers = ModifierUtility((v) => only(modifiers: v)); + IconSpecUtility(super.builder); static final self = IconSpecUtility((v) => v); @@ -273,6 +284,7 @@ base class IconSpecUtility bool? applyTextScaling, double? fill, AnimatedDataDto? animated, + WidgetModifiersDataDto? modifiers, }) { return builder(IconSpecAttribute( color: color, @@ -285,6 +297,7 @@ base class IconSpecUtility applyTextScaling: applyTextScaling, fill: fill, animated: animated, + modifiers: modifiers, )); } } diff --git a/packages/mix/lib/src/specs/icon/icon_widget.dart b/packages/mix/lib/src/specs/icon/icon_widget.dart index 56b774956..1127d1465 100644 --- a/packages/mix/lib/src/specs/icon/icon_widget.dart +++ b/packages/mix/lib/src/specs/icon/icon_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../../core/styled_widget.dart'; +import '../../modifiers/render_widget_modifier.dart'; import 'icon_spec.dart'; class StyledIcon extends StyledWidget { @@ -50,28 +51,32 @@ class IconSpecWidget extends StatelessWidget { this.semanticLabel, super.key, this.textDirection, - this.modifierOrder = const [], + this.orderOfModifiers = const [], }); final IconData? icon; final IconSpec? spec; final String? semanticLabel; final TextDirection? textDirection; - final List modifierOrder; + final List orderOfModifiers; @override Widget build(BuildContext context) { - return Icon( - icon, - size: spec?.size, - fill: spec?.fill, - weight: spec?.weight, - grade: spec?.grade, - opticalSize: spec?.opticalSize, - color: spec?.color, - shadows: spec?.shadows, - semanticLabel: semanticLabel, - textDirection: textDirection, + return RenderInlineModifiers( + orderOfModifiers: orderOfModifiers, + spec: spec ?? const IconSpec(), + child: Icon( + icon, + size: spec?.size, + fill: spec?.fill, + weight: spec?.weight, + grade: spec?.grade, + opticalSize: spec?.opticalSize, + color: spec?.color, + shadows: spec?.shadows, + semanticLabel: semanticLabel, + textDirection: textDirection, + ), ); } } @@ -117,7 +122,7 @@ class AnimatedIconSpecWidget extends ImplicitlyAnimatedWidget { super.key, this.semanticLabel, this.textDirection, - this.modifierOrder = const [], + this.orderOfModifiers = const [], Curve curve = Curves.linear, required Duration duration, VoidCallback? onEnd, @@ -127,7 +132,7 @@ class AnimatedIconSpecWidget extends ImplicitlyAnimatedWidget { final IconSpec spec; final String? semanticLabel; final TextDirection? textDirection; - final List modifierOrder; + final List orderOfModifiers; @override // ignore: library_private_types_in_public_api @@ -158,6 +163,7 @@ class _AnimatedIconSpecState spec: spec, semanticLabel: widget.semanticLabel, textDirection: widget.textDirection, + orderOfModifiers: widget.orderOfModifiers, ); } } diff --git a/packages/mix/lib/src/specs/image/image_spec.dart b/packages/mix/lib/src/specs/image/image_spec.dart index a590f6647..3dd8286a2 100644 --- a/packages/mix/lib/src/specs/image/image_spec.dart +++ b/packages/mix/lib/src/specs/image/image_spec.dart @@ -32,6 +32,7 @@ final class ImageSpec extends Spec with _$ImageSpec { this.filterQuality, this.colorBlendMode, super.animated, + super.modifiers, }); Widget call({ diff --git a/packages/mix/lib/src/specs/image/image_spec.g.dart b/packages/mix/lib/src/specs/image/image_spec.g.dart index 39d366d13..4024b7fe9 100644 --- a/packages/mix/lib/src/specs/image/image_spec.g.dart +++ b/packages/mix/lib/src/specs/image/image_spec.g.dart @@ -43,6 +43,7 @@ base mixin _$ImageSpec on Spec { FilterQuality? filterQuality, BlendMode? colorBlendMode, AnimatedData? animated, + WidgetModifiersData? modifiers, }) { return ImageSpec( width: width ?? _$this.width, @@ -55,6 +56,7 @@ base mixin _$ImageSpec on Spec { filterQuality: filterQuality ?? _$this.filterQuality, colorBlendMode: colorBlendMode ?? _$this.colorBlendMode, animated: animated ?? _$this.animated, + modifiers: modifiers ?? _$this.modifiers, ); } @@ -74,7 +76,7 @@ base mixin _$ImageSpec on Spec { /// - [AlignmentGeometry.lerp] for [alignment]. /// - [Rect.lerp] for [centerSlice]. - /// For [repeat] and [fit] and [filterQuality] and [colorBlendMode] and [animated], the interpolation is performed using a step function. + /// For [repeat] and [fit] and [filterQuality] and [colorBlendMode] and [animated] and [modifiers], the interpolation is performed using a step function. /// If [t] is less than 0.5, the value from the current [ImageSpec] is used. Otherwise, the value /// from the [other] [ImageSpec] is used. /// @@ -95,6 +97,7 @@ base mixin _$ImageSpec on Spec { filterQuality: t < 0.5 ? _$this.filterQuality : other.filterQuality, colorBlendMode: t < 0.5 ? _$this.colorBlendMode : other.colorBlendMode, animated: t < 0.5 ? _$this.animated : other.animated, + modifiers: t < 0.5 ? _$this.modifiers : other.modifiers, ); } @@ -114,6 +117,7 @@ base mixin _$ImageSpec on Spec { _$this.filterQuality, _$this.colorBlendMode, _$this.animated, + _$this.modifiers, ]; ImageSpec get _$this => this as ImageSpec; @@ -148,6 +152,7 @@ final class ImageSpecAttribute extends SpecAttribute { this.filterQuality, this.colorBlendMode, super.animated, + super.modifiers, }); /// Resolves to [ImageSpec] using the provided [MixData]. @@ -171,6 +176,7 @@ final class ImageSpecAttribute extends SpecAttribute { filterQuality: filterQuality, colorBlendMode: colorBlendMode, animated: animated?.resolve(mix) ?? mix.animation, + modifiers: modifiers?.resolve(mix), ); } @@ -197,6 +203,7 @@ final class ImageSpecAttribute extends SpecAttribute { filterQuality: other.filterQuality ?? filterQuality, colorBlendMode: other.colorBlendMode ?? colorBlendMode, animated: animated?.merge(other.animated) ?? other.animated, + modifiers: modifiers?.merge(other.modifiers) ?? other.modifiers, ); } @@ -216,6 +223,7 @@ final class ImageSpecAttribute extends SpecAttribute { filterQuality, colorBlendMode, animated, + modifiers, ]; } @@ -256,6 +264,9 @@ base class ImageSpecUtility /// Utility for defining [ImageSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); + /// Utility for defining [ImageSpecAttribute.modifiers] + late final modifiers = ModifierUtility((v) => only(modifiers: v)); + ImageSpecUtility(super.builder); static final self = ImageSpecUtility((v) => v); @@ -273,6 +284,7 @@ base class ImageSpecUtility FilterQuality? filterQuality, BlendMode? colorBlendMode, AnimatedDataDto? animated, + WidgetModifiersDataDto? modifiers, }) { return builder(ImageSpecAttribute( width: width, @@ -285,6 +297,7 @@ base class ImageSpecUtility filterQuality: filterQuality, colorBlendMode: colorBlendMode, animated: animated, + modifiers: modifiers, )); } } diff --git a/packages/mix/lib/src/specs/image/image_widget.dart b/packages/mix/lib/src/specs/image/image_widget.dart index 455a523dc..1d41c909a 100644 --- a/packages/mix/lib/src/specs/image/image_widget.dart +++ b/packages/mix/lib/src/specs/image/image_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/widgets.dart'; import '../../core/styled_widget.dart'; import '../../internal/constants.dart'; +import '../../modifiers/render_widget_modifier.dart'; import 'image_spec.dart'; class StyledImage extends StyledWidget { @@ -74,7 +75,7 @@ class StyledImage extends StyledWidget { class ImageSpecWidget extends StatelessWidget { const ImageSpecWidget({ super.key, - this.modifierOrder = const [], + this.orderOfModifiers = const [], this.spec, required this.image, this.frameBuilder, @@ -95,7 +96,7 @@ class ImageSpecWidget extends StatelessWidget { final ImageErrorWidgetBuilder? errorBuilder; final String? semanticLabel; final bool excludeFromSemantics; - final List modifierOrder; + final List orderOfModifiers; final bool gaplessPlayback; final bool isAntiAlias; final bool matchTextDirection; @@ -103,26 +104,30 @@ class ImageSpecWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Image( - image: image, - frameBuilder: frameBuilder, - loadingBuilder: loadingBuilder, - errorBuilder: errorBuilder, - semanticLabel: semanticLabel, - excludeFromSemantics: excludeFromSemantics, - width: spec?.width, - height: spec?.height, - color: spec?.color, - opacity: opacity, - colorBlendMode: spec?.colorBlendMode ?? BlendMode.clear, - fit: spec?.fit, - alignment: spec?.alignment ?? Alignment.center, - repeat: spec?.repeat ?? ImageRepeat.noRepeat, - centerSlice: spec?.centerSlice, - matchTextDirection: matchTextDirection, - gaplessPlayback: gaplessPlayback, - isAntiAlias: isAntiAlias, - filterQuality: spec?.filterQuality ?? FilterQuality.low, + return RenderInlineModifiers( + orderOfModifiers: orderOfModifiers, + spec: spec ?? const ImageSpec(), + child: Image( + image: image, + frameBuilder: frameBuilder, + loadingBuilder: loadingBuilder, + errorBuilder: errorBuilder, + semanticLabel: semanticLabel, + excludeFromSemantics: excludeFromSemantics, + width: spec?.width, + height: spec?.height, + color: spec?.color, + opacity: opacity, + colorBlendMode: spec?.colorBlendMode ?? BlendMode.clear, + fit: spec?.fit, + alignment: spec?.alignment ?? Alignment.center, + repeat: spec?.repeat ?? ImageRepeat.noRepeat, + centerSlice: spec?.centerSlice, + matchTextDirection: matchTextDirection, + gaplessPlayback: gaplessPlayback, + isAntiAlias: isAntiAlias, + filterQuality: spec?.filterQuality ?? FilterQuality.low, + ), ); } } diff --git a/packages/mix/lib/src/specs/stack/stack_spec.dart b/packages/mix/lib/src/specs/stack/stack_spec.dart index 2925b1783..a30ccf43c 100644 --- a/packages/mix/lib/src/specs/stack/stack_spec.dart +++ b/packages/mix/lib/src/specs/stack/stack_spec.dart @@ -22,6 +22,7 @@ final class StackSpec extends Spec with _$StackSpec { this.textDirection, this.clipBehavior, super.animated, + super.modifiers, }); Widget call({List children = const []}) { diff --git a/packages/mix/lib/src/specs/stack/stack_spec.g.dart b/packages/mix/lib/src/specs/stack/stack_spec.g.dart index d64b3b8e7..71151c3ee 100644 --- a/packages/mix/lib/src/specs/stack/stack_spec.g.dart +++ b/packages/mix/lib/src/specs/stack/stack_spec.g.dart @@ -38,6 +38,7 @@ base mixin _$StackSpec on Spec { TextDirection? textDirection, Clip? clipBehavior, AnimatedData? animated, + WidgetModifiersData? modifiers, }) { return StackSpec( alignment: alignment ?? _$this.alignment, @@ -45,6 +46,7 @@ base mixin _$StackSpec on Spec { textDirection: textDirection ?? _$this.textDirection, clipBehavior: clipBehavior ?? _$this.clipBehavior, animated: animated ?? _$this.animated, + modifiers: modifiers ?? _$this.modifiers, ); } @@ -61,7 +63,7 @@ base mixin _$StackSpec on Spec { /// /// - [AlignmentGeometry.lerp] for [alignment]. - /// For [fit] and [textDirection] and [clipBehavior] and [animated], the interpolation is performed using a step function. + /// For [fit] and [textDirection] and [clipBehavior] and [animated] and [modifiers], the interpolation is performed using a step function. /// If [t] is less than 0.5, the value from the current [StackSpec] is used. Otherwise, the value /// from the [other] [StackSpec] is used. /// @@ -77,6 +79,7 @@ base mixin _$StackSpec on Spec { textDirection: t < 0.5 ? _$this.textDirection : other.textDirection, clipBehavior: t < 0.5 ? _$this.clipBehavior : other.clipBehavior, animated: t < 0.5 ? _$this.animated : other.animated, + modifiers: t < 0.5 ? _$this.modifiers : other.modifiers, ); } @@ -91,6 +94,7 @@ base mixin _$StackSpec on Spec { _$this.textDirection, _$this.clipBehavior, _$this.animated, + _$this.modifiers, ]; StackSpec get _$this => this as StackSpec; @@ -115,6 +119,7 @@ final class StackSpecAttribute extends SpecAttribute { this.textDirection, this.clipBehavior, super.animated, + super.modifiers, }); /// Resolves to [StackSpec] using the provided [MixData]. @@ -133,6 +138,7 @@ final class StackSpecAttribute extends SpecAttribute { textDirection: textDirection, clipBehavior: clipBehavior, animated: animated?.resolve(mix) ?? mix.animation, + modifiers: modifiers?.resolve(mix), ); } @@ -154,6 +160,7 @@ final class StackSpecAttribute extends SpecAttribute { textDirection: other.textDirection ?? textDirection, clipBehavior: other.clipBehavior ?? clipBehavior, animated: animated?.merge(other.animated) ?? other.animated, + modifiers: modifiers?.merge(other.modifiers) ?? other.modifiers, ); } @@ -168,6 +175,7 @@ final class StackSpecAttribute extends SpecAttribute { textDirection, clipBehavior, animated, + modifiers, ]; } @@ -193,6 +201,9 @@ base class StackSpecUtility /// Utility for defining [StackSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); + /// Utility for defining [StackSpecAttribute.modifiers] + late final modifiers = ModifierUtility((v) => only(modifiers: v)); + StackSpecUtility(super.builder); static final self = StackSpecUtility((v) => v); @@ -205,6 +216,7 @@ base class StackSpecUtility TextDirection? textDirection, Clip? clipBehavior, AnimatedDataDto? animated, + WidgetModifiersDataDto? modifiers, }) { return builder(StackSpecAttribute( alignment: alignment, @@ -212,6 +224,7 @@ base class StackSpecUtility textDirection: textDirection, clipBehavior: clipBehavior, animated: animated, + modifiers: modifiers, )); } } diff --git a/packages/mix/lib/src/specs/stack/stack_widget.dart b/packages/mix/lib/src/specs/stack/stack_widget.dart index 035df7b47..7a6404f71 100644 --- a/packages/mix/lib/src/specs/stack/stack_widget.dart +++ b/packages/mix/lib/src/specs/stack/stack_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/widgets.dart'; import '../../core/styled_widget.dart'; +import '../../modifiers/render_widget_modifier.dart'; import '../box/box_spec.dart'; import '../box/box_widget.dart'; import 'stack_spec.dart'; @@ -40,20 +41,30 @@ class StyledStack extends StyledWidget { } class StackSpecWidget extends StatelessWidget { - const StackSpecWidget({this.spec, super.key, this.children}); + const StackSpecWidget({ + this.spec, + this.children, + this.orderOfModifiers = const [], + super.key, + }); final List? children; final StackSpec? spec; + final List orderOfModifiers; @override Widget build(BuildContext context) { // The Stack widget is used here, applying the resolved styles from StackSpec. - return Stack( - alignment: spec?.alignment ?? _defaultStack.alignment, - textDirection: spec?.textDirection, - fit: spec?.fit ?? _defaultStack.fit, - clipBehavior: spec?.clipBehavior ?? _defaultStack.clipBehavior, - children: children ?? const [], + return RenderInlineModifiers( + orderOfModifiers: orderOfModifiers, + spec: spec ?? const StackSpec(), + child: Stack( + alignment: spec?.alignment ?? _defaultStack.alignment, + textDirection: spec?.textDirection, + fit: spec?.fit ?? _defaultStack.fit, + clipBehavior: spec?.clipBehavior ?? _defaultStack.clipBehavior, + children: children ?? const [], + ), ); } } diff --git a/packages/mix/lib/src/specs/text/text_spec.dart b/packages/mix/lib/src/specs/text/text_spec.dart index a1e0f9a67..446ea727b 100644 --- a/packages/mix/lib/src/specs/text/text_spec.dart +++ b/packages/mix/lib/src/specs/text/text_spec.dart @@ -50,6 +50,7 @@ final class TextSpec extends Spec with _$TextSpec { this.softWrap, this.directive, super.animated, + super.modifiers, }); Widget call(String text, {String? semanticLabel, Locale? locale}) { diff --git a/packages/mix/lib/src/specs/text/text_spec.g.dart b/packages/mix/lib/src/specs/text/text_spec.g.dart index 4c0c25b3e..fb6b731fe 100644 --- a/packages/mix/lib/src/specs/text/text_spec.g.dart +++ b/packages/mix/lib/src/specs/text/text_spec.g.dart @@ -45,6 +45,7 @@ base mixin _$TextSpec on Spec { bool? softWrap, TextDirective? directive, AnimatedData? animated, + WidgetModifiersData? modifiers, }) { return TextSpec( overflow: overflow ?? _$this.overflow, @@ -59,6 +60,7 @@ base mixin _$TextSpec on Spec { softWrap: softWrap ?? _$this.softWrap, directive: directive ?? _$this.directive, animated: animated ?? _$this.animated, + modifiers: modifiers ?? _$this.modifiers, ); } @@ -77,7 +79,7 @@ base mixin _$TextSpec on Spec { /// - [MixHelpers.lerpDouble] for [textScaleFactor]. /// - [MixHelpers.lerpTextStyle] for [style]. - /// For [overflow] and [textAlign] and [maxLines] and [textWidthBasis] and [textHeightBehavior] and [textDirection] and [softWrap] and [directive] and [animated], the interpolation is performed using a step function. + /// For [overflow] and [textAlign] and [maxLines] and [textWidthBasis] and [textHeightBehavior] and [textDirection] and [softWrap] and [directive] and [animated] and [modifiers], the interpolation is performed using a step function. /// If [t] is less than 0.5, the value from the current [TextSpec] is used. Otherwise, the value /// from the [other] [TextSpec] is used. /// @@ -103,6 +105,7 @@ base mixin _$TextSpec on Spec { softWrap: t < 0.5 ? _$this.softWrap : other.softWrap, directive: t < 0.5 ? _$this.directive : other.directive, animated: t < 0.5 ? _$this.animated : other.animated, + modifiers: t < 0.5 ? _$this.modifiers : other.modifiers, ); } @@ -124,6 +127,7 @@ base mixin _$TextSpec on Spec { _$this.softWrap, _$this.directive, _$this.animated, + _$this.modifiers, ]; TextSpec get _$this => this as TextSpec; @@ -162,6 +166,7 @@ final class TextSpecAttribute extends SpecAttribute { this.softWrap, this.directive, super.animated, + super.modifiers, }); /// Resolves to [TextSpec] using the provided [MixData]. @@ -187,6 +192,7 @@ final class TextSpecAttribute extends SpecAttribute { softWrap: softWrap, directive: directive?.resolve(mix), animated: animated?.resolve(mix) ?? mix.animation, + modifiers: modifiers?.resolve(mix), ); } @@ -215,6 +221,7 @@ final class TextSpecAttribute extends SpecAttribute { softWrap: other.softWrap ?? softWrap, directive: directive?.merge(other.directive) ?? other.directive, animated: animated?.merge(other.animated) ?? other.animated, + modifiers: modifiers?.merge(other.modifiers) ?? other.modifiers, ); } @@ -236,6 +243,7 @@ final class TextSpecAttribute extends SpecAttribute { softWrap, directive, animated, + modifiers, ]; } @@ -299,6 +307,9 @@ base class TextSpecUtility /// Utility for defining [TextSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); + /// Utility for defining [TextSpecAttribute.modifiers] + late final modifiers = ModifierUtility((v) => only(modifiers: v)); + TextSpecUtility(super.builder); static final self = TextSpecUtility((v) => v); @@ -318,6 +329,7 @@ base class TextSpecUtility bool? softWrap, TextDirectiveDto? directive, AnimatedDataDto? animated, + WidgetModifiersDataDto? modifiers, }) { return builder(TextSpecAttribute( overflow: overflow, @@ -332,6 +344,7 @@ base class TextSpecUtility softWrap: softWrap, directive: directive, animated: animated, + modifiers: modifiers, )); } } diff --git a/packages/mix/lib/src/specs/text/text_widget.dart b/packages/mix/lib/src/specs/text/text_widget.dart index 79badfb37..f5ccfb3f4 100644 --- a/packages/mix/lib/src/specs/text/text_widget.dart +++ b/packages/mix/lib/src/specs/text/text_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../../core/styled_widget.dart'; +import '../../modifiers/render_widget_modifier.dart'; import 'text_spec.dart'; /// [StyledText] - A styled widget for displaying text with a mix of styles. @@ -77,6 +78,7 @@ class TextSpecWidget extends StatelessWidget { required this.spec, this.semanticsLabel, this.locale, + this.orderOfModifiers = const [], super.key, }); @@ -84,24 +86,29 @@ class TextSpecWidget extends StatelessWidget { final String? semanticsLabel; final Locale? locale; final TextSpec? spec; + final List orderOfModifiers; @override Widget build(BuildContext context) { // The Text widget is used here, applying the resolved styles and properties from TextSpec. - return Text( - spec?.directive?.apply(text) ?? text, - style: spec?.style, - strutStyle: spec?.strutStyle, - textAlign: spec?.textAlign, - textDirection: spec?.textDirection, - locale: locale, - softWrap: spec?.softWrap, - overflow: spec?.overflow, - textScaleFactor: spec?.textScaleFactor, - maxLines: spec?.maxLines, - semanticsLabel: semanticsLabel, - textWidthBasis: spec?.textWidthBasis, - textHeightBehavior: spec?.textHeightBehavior, + return RenderInlineModifiers( + orderOfModifiers: const [], + spec: spec ?? const TextSpec(), + child: Text( + spec?.directive?.apply(text) ?? text, + style: spec?.style, + strutStyle: spec?.strutStyle, + textAlign: spec?.textAlign, + textDirection: spec?.textDirection, + locale: locale, + softWrap: spec?.softWrap, + overflow: spec?.overflow, + textScaleFactor: spec?.textScaleFactor, + maxLines: spec?.maxLines, + semanticsLabel: semanticsLabel, + textWidthBasis: spec?.textWidthBasis, + textHeightBehavior: spec?.textHeightBehavior, + ), ); } } diff --git a/packages/mix/test/modifiers/render_widget_modifier_test.dart b/packages/mix/test/modifiers/render_widget_modifier_test.dart new file mode 100644 index 000000000..09df400d9 --- /dev/null +++ b/packages/mix/test/modifiers/render_widget_modifier_test.dart @@ -0,0 +1,61 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart'; + +void main() { + group('orderSpecs', () { + test('should order modifiers based on provided order', () { + final orderOfModifiers = [ + ClipOvalModifierSpec, + OpacityModifierSpec, + AlignModifierSpec, + TransformModifierSpec, + ]; + final Set modifiers = { + const OpacityModifierSpec(1), + TransformModifierSpec(transform: Matrix4.rotationX(3)), + const AlignModifierSpec(alignment: Alignment.center), + const ClipOvalModifierSpec(), + }; + + final result = orderSpecs(orderOfModifiers, modifiers); + + expect(result.map((e) => e.type), orderOfModifiers); + }); + + test('should include default order specs', () { + final Set modifiers = { + TransformModifierSpec(transform: Matrix4.rotationX(3)), + const OpacityModifierSpec(1), + const AlignModifierSpec(alignment: Alignment.center), + }; + + final result = orderSpecs([], modifiers); + + expect(result.map((e) => e.type), + [AlignModifierSpec, TransformModifierSpec, OpacityModifierSpec]); + }); + + test('should handle empty modifiers', () { + final orderOfModifiers = [TransformModifierSpec]; + final modifiers = {}; + + final result = orderSpecs(orderOfModifiers, modifiers); + + expect(result, isEmpty); + }); + + test('should handle duplicate modifiers', () { + final orderOfModifiers = [TransformModifierSpec]; + final modifiers = { + const OpacityModifierSpec(1), + const OpacityModifierSpec(0.4), + }; + + final result = orderSpecs(orderOfModifiers, modifiers); + + expect(result.length, 1); + expect(result.first.type, OpacityModifierSpec); + }); + }); +} diff --git a/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart b/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart index 3e3a17e1b..6449a7a36 100644 --- a/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart +++ b/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; -import 'package:mix/src/attributes/modifiers/modifiers_data.dart'; -import 'package:mix/src/attributes/modifiers/modifiers_data_dto.dart'; +import 'package:mix/src/attributes/modifiers/widget_modifiers_data.dart'; +import 'package:mix/src/attributes/modifiers/widget_modifiers_data_dto.dart'; import '../../../helpers/testing_utils.dart'; void main() { group('ModifiersDataDto', () { test('should create an instance with default values', () { - const dto = ModifiersDataDto([]); + const dto = WidgetModifiersDataDto([]); expect(dto.value, isEmpty); }); @@ -17,13 +17,13 @@ void main() { const modifier1 = TestModifierSpecAttribute(); const modifier2 = TestModifierSpecAttribute(); // ignore: equal_elements_in_set - const dto = ModifiersDataDto([modifier1, modifier2]); + const dto = WidgetModifiersDataDto([modifier1, modifier2]); expect(dto.value, contains(modifier1)); }); test('should merge with another instance', () { - const dto1 = ModifiersDataDto([TestModifierSpecAttribute()]); - const dto2 = ModifiersDataDto([TestModifierSpecAttribute(2)]); + const dto1 = WidgetModifiersDataDto([TestModifierSpecAttribute()]); + const dto2 = WidgetModifiersDataDto([TestModifierSpecAttribute(2)]); final merged = dto1.merge(dto2); expect(merged.value, hasLength(1)); expect(merged.value.first, const TestModifierSpecAttribute(2)); @@ -31,7 +31,7 @@ void main() { test('should resolve to a ModifiersData instance', () { const modifier = TestModifierSpecAttribute(); - const dto = ModifiersDataDto([modifier]); + const dto = WidgetModifiersDataDto([modifier]); final modifiersData = dto.resolve(EmptyMixData); expect(modifiersData.value.length, 1); expect(modifiersData.value.first, const TestModifierSpec()); @@ -39,14 +39,14 @@ void main() { // test equality test('should be equal to another instance', () { - const dto1 = ModifiersDataDto([TestModifierSpecAttribute()]); - const dto2 = ModifiersDataDto([TestModifierSpecAttribute()]); + const dto1 = WidgetModifiersDataDto([TestModifierSpecAttribute()]); + const dto2 = WidgetModifiersDataDto([TestModifierSpecAttribute()]); expect(dto1, equals(dto2)); }); test('should not be equal to another instance', () { - const dto1 = ModifiersDataDto([TestModifierSpecAttribute()]); - const dto2 = ModifiersDataDto([TestModifierSpecAttribute(2)]); + const dto1 = WidgetModifiersDataDto([TestModifierSpecAttribute()]); + const dto2 = WidgetModifiersDataDto([TestModifierSpecAttribute(2)]); expect(dto1, isNot(equals(dto2))); }); }); @@ -56,20 +56,20 @@ void main() { const modifier1 = TestModifierSpec(); const modifier2 = TestModifierSpec(); // ignore: equal_elements_in_set - const modifiersData = ModifiersData([modifier1, modifier2]); + const modifiersData = WidgetModifiersData([modifier1, modifier2]); expect(modifiersData.value, contains(modifier1)); }); // equality test('should be equal to another instance', () { - const modifiersData1 = ModifiersData([TestModifierSpec()]); - const modifiersData2 = ModifiersData([TestModifierSpec()]); + const modifiersData1 = WidgetModifiersData([TestModifierSpec()]); + const modifiersData2 = WidgetModifiersData([TestModifierSpec()]); expect(modifiersData1, equals(modifiersData2)); }); test('should not be equal to another instance', () { - const modifiersData1 = ModifiersData([TestModifierSpec()]); - const modifiersData2 = ModifiersData([]); + const modifiersData1 = WidgetModifiersData([TestModifierSpec()]); + const modifiersData2 = WidgetModifiersData([]); expect(modifiersData1, isNot(equals(modifiersData2))); }); }); diff --git a/packages/mix/test/src/specs/box/box_attribute_test.dart b/packages/mix/test/src/specs/box/box_attribute_test.dart index b4d85613d..32a8a146a 100644 --- a/packages/mix/test/src/specs/box/box_attribute_test.dart +++ b/packages/mix/test/src/specs/box/box_attribute_test.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; -import 'package:mix/src/attributes/modifiers/modifiers_data_dto.dart'; +import 'package:mix/src/attributes/modifiers/widget_modifiers_data_dto.dart'; import '../../../helpers/testing_utils.dart'; @@ -23,7 +23,7 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), ]), @@ -54,7 +54,7 @@ void main() { expect(containerSpecAttribute.width, 100); expect( containerSpecAttribute.modifiers, - const ModifiersDataDto([ + const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), ])); @@ -77,7 +77,7 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), ])); @@ -127,7 +127,7 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), ])); @@ -148,7 +148,7 @@ void main() { clipBehavior: Clip.antiAliasWithSaveLayer, width: 200, height: 200, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ SizedBoxModifierAttribute(width: 20), ]), ), @@ -179,7 +179,7 @@ void main() { expect(mergedBoxSpecAttribute.width, 200); expect( mergedBoxSpecAttribute.modifiers, - const ModifiersDataDto([ + const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 20), ])); @@ -202,7 +202,7 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), ])); @@ -225,7 +225,7 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, - modifiers: const ModifiersDataDto( + modifiers: const WidgetModifiersDataDto( [ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), @@ -279,7 +279,7 @@ void main() { clipBehavior: Clip.antiAliasWithSaveLayer, width: 200, height: 200, - modifiers: const ModifiersDataDto( + modifiers: const WidgetModifiersDataDto( [ OpacityModifierAttribute(0.4), SizedBoxModifierAttribute(height: 20, width: 10), diff --git a/packages/mix/test/src/specs/box/box_spec_test.dart b/packages/mix/test/src/specs/box/box_spec_test.dart index b36f3aaad..c666ae629 100644 --- a/packages/mix/test/src/specs/box/box_spec_test.dart +++ b/packages/mix/test/src/specs/box/box_spec_test.dart @@ -3,8 +3,8 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mix/mix.dart'; -import 'package:mix/src/attributes/modifiers/modifiers_data.dart'; -import 'package:mix/src/attributes/modifiers/modifiers_data_dto.dart'; +import 'package:mix/src/attributes/modifiers/widget_modifiers_data.dart'; +import 'package:mix/src/attributes/modifiers/widget_modifiers_data_dto.dart'; import '../../../helpers/testing_utils.dart'; @@ -23,7 +23,7 @@ void main() { decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), transform: Matrix4.translationValues(10.0, 10.0, 0.0), clipBehavior: Clip.antiAlias, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ OpacityModifierAttribute(1), SizedBoxModifierAttribute(height: 10, width: 10), ]), @@ -65,7 +65,7 @@ void main() { transformAlignment: Alignment.center, width: 300, height: 200, - modifiers: const ModifiersData([ + modifiers: const WidgetModifiersData([ OpacityModifierSpec(0.5), SizedBoxModifierSpec(height: 10, width: 10), ]), @@ -74,7 +74,7 @@ void main() { final copiedSpec = spec.copyWith( width: 250.0, height: 150.0, - modifiers: const ModifiersData([ + modifiers: const WidgetModifiersData([ OpacityModifierSpec(1), ]), ); @@ -98,7 +98,7 @@ void main() { expect( copiedSpec.modifiers!.value, - const ModifiersData( + const WidgetModifiersData( [OpacityModifierSpec(1)], ).value, ); @@ -206,7 +206,7 @@ void main() { clipBehavior: Clip.none, width: 300, height: 200, - modifiers: const ModifiersData([ + modifiers: const WidgetModifiersData([ OpacityModifierSpec(0.5), SizedBoxModifierSpec(height: 10, width: 10), ]), @@ -224,7 +224,7 @@ void main() { clipBehavior: Clip.none, width: 300, height: 200, - modifiers: const ModifiersData([ + modifiers: const WidgetModifiersData([ OpacityModifierSpec(0.5), SizedBoxModifierSpec(height: 10, width: 10), ]), @@ -252,7 +252,7 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ OpacityModifierAttribute(0.5), SizedBoxModifierAttribute(height: 10, width: 10), ]), @@ -276,7 +276,7 @@ void main() { clipBehavior: Clip.antiAliasWithSaveLayer, width: 200, height: 200, - modifiers: const ModifiersDataDto([ + modifiers: const WidgetModifiersDataDto([ SizedBoxModifierAttribute(width: 100), ]), ), diff --git a/packages/mix/test/src/specs/image/image_spec_test.dart b/packages/mix/test/src/specs/image/image_spec_test.dart index 4a98034a5..887b94873 100644 --- a/packages/mix/test/src/specs/image/image_spec_test.dart +++ b/packages/mix/test/src/specs/image/image_spec_test.dart @@ -114,8 +114,9 @@ void main() { expect(getValueOf(spec.centerSlice), Rect.zero); expect(getValueOf(spec.filterQuality), FilterQuality.low); expect(getValueOf(spec.colorBlendMode), BlendMode.srcOver); + expect(getValueOf(spec.modifiers), null); expect(getValueOf(spec.animated), const AnimatedData.withDefaults()); - expect(spec.props.length, 10); + expect(spec.props.length, 11); }); }); } diff --git a/packages/mix/test/src/specs/stack/stack_attribute_test.dart b/packages/mix/test/src/specs/stack/stack_attribute_test.dart index f9dee178d..227b87371 100644 --- a/packages/mix/test/src/specs/stack/stack_attribute_test.dart +++ b/packages/mix/test/src/specs/stack/stack_attribute_test.dart @@ -70,7 +70,7 @@ void main() { ); final props = attribute.props; - expect(props.length, 5); + expect(props.length, 6); expect(props[0], Alignment.center); expect(props[1], StackFit.expand); expect(props[2], TextDirection.ltr); diff --git a/packages/mix/test/src/widgets/box/box_test.dart b/packages/mix/test/src/widgets/box/box_test.dart index 2369d8b02..6369a5953 100644 --- a/packages/mix/test/src/widgets/box/box_test.dart +++ b/packages/mix/test/src/widgets/box/box_test.dart @@ -151,7 +151,7 @@ void main() { of: find.byKey(key), matching: find.byType(RenderModifiers), ), - findsOneWidget, + findsNWidgets(2), ); expect( diff --git a/packages/mix/test/src/widgets/styled_icon_test.dart b/packages/mix/test/src/widgets/styled_icon_test.dart index be4f93bea..a5a52724c 100644 --- a/packages/mix/test/src/widgets/styled_icon_test.dart +++ b/packages/mix/test/src/widgets/styled_icon_test.dart @@ -51,7 +51,7 @@ void main() { of: find.byKey(key), matching: find.byType(RenderModifiers), ), - findsOneWidget, + findsNWidgets(2), ); expect( diff --git a/packages/mix_generator/lib/src/helpers/type_ref_repository.dart b/packages/mix_generator/lib/src/helpers/type_ref_repository.dart index ea4242092..899db24ce 100644 --- a/packages/mix_generator/lib/src/helpers/type_ref_repository.dart +++ b/packages/mix_generator/lib/src/helpers/type_ref_repository.dart @@ -16,6 +16,7 @@ class TypeRefRepository { static Map _utilityOverrides = { 'EdgeInsetsGeometry': 'SpacingUtility', 'AnimatedData': 'AnimatedUtility', + 'WidgetModifiersData': 'ModifierUtility', }; static final _dtoMap = { @@ -23,7 +24,7 @@ class TypeRefRepository { 'BoxConstraints': 'BoxConstraintsDto', 'Decoration': 'DecorationDto', 'Color': 'ColorDto', - 'ModifiersData': 'ModifiersDataDto', + 'WidgetModifiersData': 'WidgetModifiersDataDto', 'AnimatedData': 'AnimatedDataDto', 'TextStyle': 'TextStyleDto', 'ShapeBorder': 'ShapeBorderDto',