diff --git a/melos.yaml b/melos.yaml index 56d28de3a..cb3067e0e 100644 --- a/melos.yaml +++ b/melos.yaml @@ -1,6 +1,6 @@ name: mix_workspace -sdkPath: .fvm/flutter_sdk +# sdkPath: .fvm/flutter_sdk packages: - packages/** - examples/** diff --git a/packages/mix/lib/src/attributes/modifiers/modifiers_data.dart b/packages/mix/lib/src/attributes/modifiers/modifiers_data.dart new file mode 100644 index 000000000..d151ce7d6 --- /dev/null +++ b/packages/mix/lib/src/attributes/modifiers/modifiers_data.dart @@ -0,0 +1,7 @@ +import '../../core/modifier.dart'; + +class ModifiersData { + // ignore: avoid-dynamic + final List> value; + const ModifiersData(this.value); +} diff --git a/packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart b/packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart new file mode 100644 index 000000000..ff24be7ef --- /dev/null +++ b/packages/mix/lib/src/attributes/modifiers/modifiers_data_dto.dart @@ -0,0 +1,29 @@ +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_util.dart b/packages/mix/lib/src/attributes/modifiers/modifiers_util.dart new file mode 100644 index 000000000..83902103e --- /dev/null +++ b/packages/mix/lib/src/attributes/modifiers/modifiers_util.dart @@ -0,0 +1,38 @@ +import '../../core/core.dart'; +import '../../modifiers/modifiers.dart'; +import '../spacing/spacing_util.dart'; +import 'modifiers_data_dto.dart'; + +final class ModifiersDataUtility + extends MixUtility { + late final add = WithModifierUtility((v) => builder(ModifiersDataDto([v]))); + + late final intrinsicWidth = IntrinsicWidthWidgetUtility(only); + late final intrinsicHeight = IntrinsicHeightWidgetUtility(only); + late final rotate = RotatedBoxWidgetUtility(only); + late final opacity = OpacityUtility(only); + late final clipPath = ClipPathUtility(only); + late final clipRRect = ClipRRectUtility(only); + late final clipOval = ClipOvalUtility(only); + late final clipRect = ClipRectUtility(only); + late final clipTriangle = ClipTriangleUtility(only); + late final visibility = VisibilityUtility(only); + late final show = visibility.on; + late final hide = visibility.off; + late final aspectRatio = AspectRatioUtility(only); + late final flexible = FlexibleModifierUtility(only); + late final expanded = flexible.expanded; + late final transform = TransformUtility(only); + + late final scale = transform.scale; + late final align = AlignWidgetUtility(only); + late final fractionallySizedBox = FractionallySizedBoxModifierUtility(only); + late final sizedBox = SizedBoxModifierUtility(only); + late final padding = SpacingUtility(PaddingModifierUtility(only)); + + ModifiersDataUtility(super.builder); + + T only(WidgetModifierAttribute attribute) { + return builder(ModifiersDataDto([attribute])); + } +} diff --git a/packages/mix/lib/src/core/spec.dart b/packages/mix/lib/src/core/spec.dart index fbf1fc4a7..9e39863a4 100644 --- a/packages/mix/lib/src/core/spec.dart +++ b/packages/mix/lib/src/core/spec.dart @@ -2,6 +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 '../internal/compare_mixin.dart'; import 'attribute.dart'; import 'factory/mix_data.dart'; @@ -10,8 +12,9 @@ import 'utility.dart'; @immutable abstract class Spec> with EqualityMixin { final AnimatedData? animated; + final ModifiersData? modifiers; - const Spec({this.animated}); + const Spec({this.animated, this.modifiers}); Type get type => T; @@ -31,8 +34,9 @@ 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; - const SpecAttribute({this.animated}); + const SpecAttribute({this.animated, this.modifiers}); Value resolve(MixData mix); diff --git a/packages/mix/lib/src/core/styled_widget.dart b/packages/mix/lib/src/core/styled_widget.dart index e029e5ee2..4eeaf462a 100644 --- a/packages/mix/lib/src/core/styled_widget.dart +++ b/packages/mix/lib/src/core/styled_widget.dart @@ -62,15 +62,13 @@ abstract class StyledWidget extends StatelessWidget { Widget applyModifiers(MixData mix, Widget child) { return mix.isAnimated ? RenderAnimatedModifiers( - mix: mix, - orderOfModifiers: orderOfModifiers, + modifiers: resolveModifierSpecs(orderOfModifiers, mix), duration: mix.animation!.duration, curve: mix.animation!.curve, child: child, ) : RenderModifiers( - mix: mix, - orderOfModifiers: orderOfModifiers, + modifiers: resolveModifierSpecs(orderOfModifiers, mix), child: child, ); } diff --git a/packages/mix/lib/src/modifiers/render_widget_modifier.dart b/packages/mix/lib/src/modifiers/render_widget_modifier.dart index b1249188e..d4cdbc527 100644 --- a/packages/mix/lib/src/modifiers/render_widget_modifier.dart +++ b/packages/mix/lib/src/modifiers/render_widget_modifier.dart @@ -1,10 +1,11 @@ // ignore_for_file: avoid-dynamic -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import '../core/attributes_map.dart'; import '../core/factory/mix_data.dart'; import '../core/modifier.dart'; +import '../core/spec.dart'; import 'align_widget_modifier.dart'; import 'aspect_ratio_widget_modifier.dart'; import 'clip_widget_modifier.dart'; @@ -71,23 +72,19 @@ const _defaultOrder = [ class RenderModifiers extends StatelessWidget { const RenderModifiers({ - required this.mix, required this.child, + required this.modifiers, super.key, - required this.orderOfModifiers, }); - final MixData mix; final Widget child; - final List orderOfModifiers; + final Set> modifiers; @override Widget build(BuildContext context) { - final specs = resolveModifierSpecs(orderOfModifiers, mix); - var current = child; - for (final spec in specs) { + for (final spec in modifiers) { current = spec.build(current); } @@ -97,18 +94,17 @@ class RenderModifiers extends StatelessWidget { class RenderAnimatedModifiers extends ImplicitlyAnimatedWidget { const RenderAnimatedModifiers({ - required this.mix, + required this.modifiers, required this.child, - required this.orderOfModifiers, - super.key, required super.duration, + super.key, super.curve = Curves.linear, super.onEnd, }); - final MixData mix; final Widget child; - final List orderOfModifiers; + + final Set> modifiers; @override RenderAnimatedModifiersState createState() => RenderAnimatedModifiersState(); @@ -120,12 +116,9 @@ class RenderAnimatedModifiersState @override void forEachTween(TweenVisitor visitor) { - final specs = resolveModifierSpecs(widget.orderOfModifiers, widget.mix); - - for (final spec in specs) { + for (final spec in widget.modifiers) { final specType = spec.runtimeType; final previousSpec = _specs[specType]; - _specs[specType] = visitor( previousSpec, spec, @@ -155,6 +148,15 @@ Set resolveModifierSpecs( final modifiers = mix.whereType(); if (modifiers.isEmpty) return {}; + + return orderModifierSpecs(orderOfModifiers, mix, modifiers); +} + +Set orderModifierSpecs( + List orderOfModifiers, + MixData mix, + Iterable modifiers, +) { final modifierMap = AttributeMap(modifiers).toMap(); final listOfModifiers = { @@ -178,6 +180,36 @@ Set resolveModifierSpecs( return specs.toSet(); } +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; + + @override + Widget build(BuildContext context) { + return spec.isAnimated + ? RenderAnimatedModifiers( + modifiers: spec.modifiers?.value.toSet() ?? {}, + duration: spec.animated!.duration, + curve: spec.animated!.curve, + child: child, + ) + : RenderModifiers( + modifiers: spec.modifiers?.value.toSet() ?? {}, + child: child, + ); + } +} + class ModifierSpecTween extends Tween { /// Creates an [EdgeInsetsGeometry] tween. /// diff --git a/packages/mix/lib/src/specs/box/box_spec.dart b/packages/mix/lib/src/specs/box/box_spec.dart index ef3a31a13..115158fca 100644 --- a/packages/mix/lib/src/specs/box/box_spec.dart +++ b/packages/mix/lib/src/specs/box/box_spec.dart @@ -3,6 +3,10 @@ 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( @@ -96,6 +100,7 @@ final class BoxSpec extends Spec with _$BoxSpec { this.clipBehavior, this.width, this.height, + super.modifiers, super.animated, }); 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 35620124c..94ad86b3a 100644 --- a/packages/mix/lib/src/specs/box/box_spec.g.dart +++ b/packages/mix/lib/src/specs/box/box_spec.g.dart @@ -43,6 +43,7 @@ base mixin _$BoxSpec on Spec { Clip? clipBehavior, double? width, double? height, + ModifiersData? modifiers, AnimatedData? animated, }) { return BoxSpec( @@ -57,6 +58,7 @@ base mixin _$BoxSpec on Spec { clipBehavior: clipBehavior ?? _$this.clipBehavior, width: width ?? _$this.width, height: height ?? _$this.height, + modifiers: modifiers ?? _$this.modifiers, animated: animated ?? _$this.animated, ); } @@ -79,7 +81,7 @@ base mixin _$BoxSpec on Spec { /// - [MixHelpers.lerpMatrix4] for [transform]. /// - [MixHelpers.lerpDouble] for [width] and [height]. - /// For [clipBehavior] and [animated], the interpolation is performed using a step function. + /// For [clipBehavior] and [modifiers] and [animated], the interpolation is performed using a step function. /// If [t] is less than 0.5, the value from the current [BoxSpec] is used. Otherwise, the value /// from the [other] [BoxSpec] is used. /// @@ -104,6 +106,7 @@ base mixin _$BoxSpec on Spec { clipBehavior: t < 0.5 ? _$this.clipBehavior : other.clipBehavior, width: MixHelpers.lerpDouble(_$this.width, other.width, t), height: MixHelpers.lerpDouble(_$this.height, other.height, t), + modifiers: t < 0.5 ? _$this.modifiers : other.modifiers, animated: t < 0.5 ? _$this.animated : other.animated, ); } @@ -125,6 +128,7 @@ base mixin _$BoxSpec on Spec { _$this.clipBehavior, _$this.width, _$this.height, + _$this.modifiers, _$this.animated, ]; @@ -163,6 +167,7 @@ final class BoxSpecAttribute extends SpecAttribute { this.clipBehavior, this.width, this.height, + super.modifiers, super.animated, }); @@ -188,6 +193,7 @@ final class BoxSpecAttribute extends SpecAttribute { clipBehavior: clipBehavior, width: width, height: height, + modifiers: modifiers?.resolve(mix), animated: animated?.resolve(mix) ?? mix.animation, ); } @@ -217,6 +223,7 @@ final class BoxSpecAttribute extends SpecAttribute { clipBehavior: other.clipBehavior ?? clipBehavior, width: other.width ?? width, height: other.height ?? height, + modifiers: modifiers?.merge(other.modifiers) ?? other.modifiers, animated: animated?.merge(other.animated) ?? other.animated, ); } @@ -238,6 +245,7 @@ final class BoxSpecAttribute extends SpecAttribute { clipBehavior, width, height, + modifiers, animated, ]; } @@ -338,6 +346,9 @@ base class BoxSpecUtility /// Utility for defining [BoxSpecAttribute.height] late final height = DoubleUtility((v) => only(height: v)); + /// Utility for defining [BoxSpecAttribute.modifiers] + late final modifiers = ModifiersDataUtility((v) => only(modifiers: v)); + /// Utility for defining [BoxSpecAttribute.animated] late final animated = AnimatedUtility((v) => only(animated: v)); @@ -359,6 +370,7 @@ base class BoxSpecUtility Clip? clipBehavior, double? width, double? height, + ModifiersDataDto? modifiers, AnimatedDataDto? animated, }) { return builder(BoxSpecAttribute( @@ -373,6 +385,7 @@ base class BoxSpecUtility clipBehavior: clipBehavior, width: width, height: height, + modifiers: modifiers, animated: animated, )); } diff --git a/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart b/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart new file mode 100644 index 000000000..3e3a17e1b --- /dev/null +++ b/packages/mix/test/src/attributes/modifiers/modifiers_data_test.dart @@ -0,0 +1,119 @@ +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 '../../../helpers/testing_utils.dart'; + +void main() { + group('ModifiersDataDto', () { + test('should create an instance with default values', () { + const dto = ModifiersDataDto([]); + expect(dto.value, isEmpty); + }); + + test('should create an instance with provided values', () { + const modifier1 = TestModifierSpecAttribute(); + const modifier2 = TestModifierSpecAttribute(); + // ignore: equal_elements_in_set + const dto = ModifiersDataDto([modifier1, modifier2]); + expect(dto.value, contains(modifier1)); + }); + + test('should merge with another instance', () { + const dto1 = ModifiersDataDto([TestModifierSpecAttribute()]); + const dto2 = ModifiersDataDto([TestModifierSpecAttribute(2)]); + final merged = dto1.merge(dto2); + expect(merged.value, hasLength(1)); + expect(merged.value.first, const TestModifierSpecAttribute(2)); + }); + + test('should resolve to a ModifiersData instance', () { + const modifier = TestModifierSpecAttribute(); + const dto = ModifiersDataDto([modifier]); + final modifiersData = dto.resolve(EmptyMixData); + expect(modifiersData.value.length, 1); + expect(modifiersData.value.first, const TestModifierSpec()); + }); + + // test equality + test('should be equal to another instance', () { + const dto1 = ModifiersDataDto([TestModifierSpecAttribute()]); + const dto2 = ModifiersDataDto([TestModifierSpecAttribute()]); + expect(dto1, equals(dto2)); + }); + + test('should not be equal to another instance', () { + const dto1 = ModifiersDataDto([TestModifierSpecAttribute()]); + const dto2 = ModifiersDataDto([TestModifierSpecAttribute(2)]); + expect(dto1, isNot(equals(dto2))); + }); + }); + + group('ModifiersData', () { + test('should create an instance with provided values', () { + const modifier1 = TestModifierSpec(); + const modifier2 = TestModifierSpec(); + // ignore: equal_elements_in_set + const modifiersData = ModifiersData([modifier1, modifier2]); + expect(modifiersData.value, contains(modifier1)); + }); + + // equality + test('should be equal to another instance', () { + const modifiersData1 = ModifiersData([TestModifierSpec()]); + const modifiersData2 = ModifiersData([TestModifierSpec()]); + expect(modifiersData1, equals(modifiersData2)); + }); + + test('should not be equal to another instance', () { + const modifiersData1 = ModifiersData([TestModifierSpec()]); + const modifiersData2 = ModifiersData([]); + expect(modifiersData1, isNot(equals(modifiersData2))); + }); + }); +} + +final class TestModifierSpec extends WidgetModifierSpec { + const TestModifierSpec([this.value = 1]); + final int value; + @override + Widget build(Widget child) { + throw UnimplementedError(); + } + + @override + TestModifierSpec lerp(TestModifierSpec other, double t) { + return this; + } + + @override + TestModifierSpec copyWith() { + throw UnimplementedError(); + } + + @override + List get props => [value]; +} + +final class TestModifierSpecAttribute extends WidgetModifierAttribute< + TestModifierSpecAttribute, TestModifierSpec> { + const TestModifierSpecAttribute([this.value = 1]); + + final int value; + + @override + List get props => [value]; + + @override + SpecAttribute merge( + covariant SpecAttribute? other) { + return other ?? this; + } + + @override + TestModifierSpec resolve(MixData mix) { + return const TestModifierSpec(); + } +} diff --git a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart index c89d30df7..6446669da 100644 --- a/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart +++ b/packages/mix/test/src/modifiers/widget_modifier_widget_test.dart @@ -20,8 +20,7 @@ void main() { testWidgets('Renders modifiers in the correct order', (tester) async { await tester.pumpMaterialApp( RenderModifiers( - mix: mixData, - orderOfModifiers: const [], + modifiers: resolveModifierSpecs(const [], mixData), child: const Text('child'), ), ); @@ -83,8 +82,7 @@ void main() { await tester.pumpMaterialApp( RenderModifiers( - mix: mixData, - orderOfModifiers: const [], + modifiers: resolveModifierSpecs(const [], mixData), child: const Text('child'), ), ); @@ -98,8 +96,7 @@ void main() { await tester.pumpMaterialApp( RenderModifiers( - mix: mixData, - orderOfModifiers: const [], + modifiers: resolveModifierSpecs(const [], mixData), child: const Text('child'), ), ); @@ -113,14 +110,13 @@ void main() { (tester) async { await tester.pumpMaterialApp( RenderModifiers( - mix: mixData, - orderOfModifiers: const [ + modifiers: resolveModifierSpecs([ ClipOvalModifierAttribute, AspectRatioModifierAttribute, TransformModifierAttribute, OpacityModifierAttribute, VisibilityModifierAttribute, - ], + ], mixData), child: const Text('child'), ), ); @@ -183,11 +179,9 @@ void main() { (tester) async { await tester.pumpMaterialApp( RenderModifiers( - mix: mixData, - orderOfModifiers: const [ - ClipOvalModifierAttribute, - AspectRatioModifierAttribute - ], + modifiers: resolveModifierSpecs( + [ClipOvalModifierAttribute, AspectRatioModifierAttribute], + mixData), child: const Text('child'), ), ); @@ -259,8 +253,7 @@ void main() { await tester.pumpMaterialApp( RenderAnimatedModifiers( - mix: mixData, - orderOfModifiers: const [], + modifiers: resolveModifierSpecs(const [], mixData), duration: const Duration(milliseconds: 300), child: const Text('child'), ), diff --git a/packages/mix/test/src/modifiers/widget_modifiers_util_test.dart b/packages/mix/test/src/modifiers/widget_modifiers_util_test.dart index 1cea84330..d7ec04819 100644 --- a/packages/mix/test/src/modifiers/widget_modifiers_util_test.dart +++ b/packages/mix/test/src/modifiers/widget_modifiers_util_test.dart @@ -430,10 +430,12 @@ class _TestableRenderModifier extends StatelessWidget { @override Widget build(BuildContext context) { return RenderModifiers( - orderOfModifiers: const [], - mix: MixData.create( - context, - style, + modifiers: resolveModifierSpecs( + const [], + MixData.create( + context, + style, + ), ), child: Container(), ); 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 ffeb74d33..b4d85613d 100644 --- a/packages/mix/test/src/specs/box/box_attribute_test.dart +++ b/packages/mix/test/src/specs/box/box_attribute_test.dart @@ -1,6 +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 '../../../helpers/testing_utils.dart'; @@ -22,6 +23,10 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, + modifiers: const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ]), ); expect(containerSpecAttribute.alignment, Alignment.center); @@ -47,26 +52,35 @@ void main() { ); expect(containerSpecAttribute.transform, Matrix4.identity()); expect(containerSpecAttribute.width, 100); + expect( + containerSpecAttribute.modifiers, + const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ])); }); // resolve() test('resolve() returns correct instance', () { final containerSpecAttribute = BoxSpecAttribute( - alignment: Alignment.center, - padding: SpacingDto.only(top: 20, bottom: 20, left: 20, right: 20), - margin: SpacingDto.only( - top: 10, - bottom: 10, - left: 10, - right: 10, - ), - constraints: const BoxConstraintsDto(maxHeight: 100), - decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), - transform: Matrix4.identity(), - clipBehavior: Clip.antiAlias, - width: 100, - height: 100, - ); + alignment: Alignment.center, + padding: SpacingDto.only(top: 20, bottom: 20, left: 20, right: 20), + margin: SpacingDto.only( + top: 10, + bottom: 10, + left: 10, + right: 10, + ), + constraints: const BoxConstraintsDto(maxHeight: 100), + decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), + transform: Matrix4.identity(), + clipBehavior: Clip.antiAlias, + width: 100, + height: 100, + modifiers: const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ])); final containerSpec = containerSpecAttribute.resolve(EmptyMixData); @@ -90,26 +104,33 @@ void main() { ); expect(containerSpec.transform, Matrix4.identity()); expect(containerSpec.width, 100); + expect(containerSpec.modifiers!.value, [ + const OpacityModifierSpec(0.5), + const SizedBoxModifierSpec(height: 10, width: 10), + ]); }); // merge() test('merge() returns correct instance', () { final containerSpecAttribute = BoxSpecAttribute( - alignment: Alignment.center, - padding: SpacingDto.only(top: 20, bottom: 20, left: 20, right: 20), - margin: SpacingDto.only( - top: 10, - bottom: 10, - left: 10, - right: 10, - ), - constraints: const BoxConstraintsDto(maxHeight: 100), - decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), - transform: Matrix4.identity(), - clipBehavior: Clip.antiAlias, - width: 100, - height: 100, - ); + alignment: Alignment.center, + padding: SpacingDto.only(top: 20, bottom: 20, left: 20, right: 20), + margin: SpacingDto.only( + top: 10, + bottom: 10, + left: 10, + right: 10, + ), + constraints: const BoxConstraintsDto(maxHeight: 100), + decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), + transform: Matrix4.identity(), + clipBehavior: Clip.antiAlias, + width: 100, + height: 100, + modifiers: const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ])); final mergedBoxSpecAttribute = containerSpecAttribute.merge( BoxSpecAttribute( @@ -127,6 +148,9 @@ void main() { clipBehavior: Clip.antiAliasWithSaveLayer, width: 200, height: 200, + modifiers: const ModifiersDataDto([ + SizedBoxModifierAttribute(width: 20), + ]), ), ); @@ -153,26 +177,35 @@ void main() { ); expect(mergedBoxSpecAttribute.transform, Matrix4.identity()); expect(mergedBoxSpecAttribute.width, 200); + expect( + mergedBoxSpecAttribute.modifiers, + const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 20), + ])); }); // equality test('equality', () { final containerSpecAttribute = BoxSpecAttribute( - alignment: Alignment.center, - padding: SpacingDto.only(top: 20, bottom: 20, left: 20, right: 20), - margin: SpacingDto.only( - top: 10, - bottom: 10, - left: 10, - right: 10, - ), - constraints: const BoxConstraintsDto(maxHeight: 100), - decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), - transform: Matrix4.identity(), - clipBehavior: Clip.antiAlias, - width: 100, - height: 100, - ); + alignment: Alignment.center, + padding: SpacingDto.only(top: 20, bottom: 20, left: 20, right: 20), + margin: SpacingDto.only( + top: 10, + bottom: 10, + left: 10, + right: 10, + ), + constraints: const BoxConstraintsDto(maxHeight: 100), + decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), + transform: Matrix4.identity(), + clipBehavior: Clip.antiAlias, + width: 100, + height: 100, + modifiers: const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ])); expect( containerSpecAttribute, @@ -192,6 +225,12 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, + modifiers: const ModifiersDataDto( + [ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ], + ), ), ), ); @@ -240,6 +279,12 @@ void main() { clipBehavior: Clip.antiAliasWithSaveLayer, width: 200, height: 200, + modifiers: const ModifiersDataDto( + [ + 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 46e2267c5..b36f3aaad 100644 --- a/packages/mix/test/src/specs/box/box_spec_test.dart +++ b/packages/mix/test/src/specs/box/box_spec_test.dart @@ -3,6 +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 '../../../helpers/testing_utils.dart'; @@ -21,6 +23,10 @@ void main() { decoration: const BoxDecorationDto(color: ColorDto(Colors.blue)), transform: Matrix4.translationValues(10.0, 10.0, 0.0), clipBehavior: Clip.antiAlias, + modifiers: const ModifiersDataDto([ + OpacityModifierAttribute(1), + SizedBoxModifierAttribute(height: 10, width: 10), + ]), width: 300, height: 200, ), @@ -39,6 +45,10 @@ void main() { expect(spec.decoration, const BoxDecoration(color: Colors.blue)); expect(spec.transform, Matrix4.translationValues(10.0, 10.0, 0.0)); + expect(spec.modifiers!.value, [ + const OpacityModifierSpec(1), + const SizedBoxModifierSpec(height: 10, width: 10), + ]); expect(spec.clipBehavior, Clip.antiAlias); }); @@ -55,9 +65,19 @@ void main() { transformAlignment: Alignment.center, width: 300, height: 200, + modifiers: const ModifiersData([ + OpacityModifierSpec(0.5), + SizedBoxModifierSpec(height: 10, width: 10), + ]), ); - final copiedSpec = spec.copyWith(width: 250.0, height: 150.0); + final copiedSpec = spec.copyWith( + width: 250.0, + height: 150.0, + modifiers: const ModifiersData([ + OpacityModifierSpec(1), + ]), + ); expect(copiedSpec.alignment, Alignment.center); expect(copiedSpec.padding, const EdgeInsets.all(16.0)); @@ -75,6 +95,13 @@ void main() { expect(copiedSpec.transform, Matrix4.translationValues(10.0, 10.0, 0.0)); expect(copiedSpec.clipBehavior, Clip.antiAlias); expect(copiedSpec.width, 250.0); + + expect( + copiedSpec.modifiers!.value, + const ModifiersData( + [OpacityModifierSpec(1)], + ).value, + ); expect(copiedSpec.height, 150.0); }); @@ -179,6 +206,10 @@ void main() { clipBehavior: Clip.none, width: 300, height: 200, + modifiers: const ModifiersData([ + OpacityModifierSpec(0.5), + SizedBoxModifierSpec(height: 10, width: 10), + ]), ); final spec2 = BoxSpec( @@ -193,6 +224,10 @@ void main() { clipBehavior: Clip.none, width: 300, height: 200, + modifiers: const ModifiersData([ + OpacityModifierSpec(0.5), + SizedBoxModifierSpec(height: 10, width: 10), + ]), ); expect(spec1, spec2); @@ -217,6 +252,10 @@ void main() { clipBehavior: Clip.antiAlias, width: 100, height: 100, + modifiers: const ModifiersDataDto([ + OpacityModifierAttribute(0.5), + SizedBoxModifierAttribute(height: 10, width: 10), + ]), ); final mergedBoxSpecAttribute = containerSpecAttribute.merge( @@ -237,6 +276,9 @@ void main() { clipBehavior: Clip.antiAliasWithSaveLayer, width: 200, height: 200, + modifiers: const ModifiersDataDto([ + SizedBoxModifierAttribute(width: 100), + ]), ), ); @@ -266,6 +308,13 @@ void main() { ); expect(mergedBoxSpecAttribute.transform, Matrix4.identity()); expect(mergedBoxSpecAttribute.width, 200); + expect( + mergedBoxSpecAttribute.modifiers!.value, + [ + const OpacityModifierAttribute(0.5), + const SizedBoxModifierAttribute(height: 10, width: 100), + ], + ); }); }); } 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 b796dbd2d..ea4242092 100644 --- a/packages/mix_generator/lib/src/helpers/type_ref_repository.dart +++ b/packages/mix_generator/lib/src/helpers/type_ref_repository.dart @@ -23,6 +23,7 @@ class TypeRefRepository { 'BoxConstraints': 'BoxConstraintsDto', 'Decoration': 'DecorationDto', 'Color': 'ColorDto', + 'ModifiersData': 'ModifiersDataDto', 'AnimatedData': 'AnimatedDataDto', 'TextStyle': 'TextStyleDto', 'ShapeBorder': 'ShapeBorderDto',