From a382f5e098d2deb85e367b9257a1664005f884e8 Mon Sep 17 00:00:00 2001 From: Pedro Bissonho Date: Mon, 26 Sep 2022 07:44:14 -0300 Subject: [PATCH 1/9] add mix attribute extension --- lib/mix.dart | 4 +- lib/src/attributes/common/attribute.dart | 27 ++ lib/src/mixer/mix_context.dart | 24 ++ lib/src/mixer/mix_factory.dart | 4 + lib/src/widgets/box.widget.dart | 1 + ..._context_builder.dart => mix_builder.dart} | 9 +- lib/src/widgets/mixable.widget.dart | 61 ++++- test/widgets/extension_test.dart | 254 ++++++++++++++++++ 8 files changed, 378 insertions(+), 6 deletions(-) rename lib/src/widgets/{mix_context_builder.dart => mix_builder.dart} (86%) create mode 100644 test/widgets/extension_test.dart diff --git a/lib/mix.dart b/lib/mix.dart index cf71ee873..04ddf9205 100644 --- a/lib/mix.dart +++ b/lib/mix.dart @@ -5,7 +5,7 @@ export 'package:mix/src/widgets/box.widget.dart'; export 'package:mix/src/widgets/flex.widget.dart'; export 'package:mix/src/widgets/gap.widget.dart'; export 'package:mix/src/widgets/icon.widget.dart'; -export 'package:mix/src/widgets/mix_context_builder.dart'; +export 'package:mix/src/widgets/mix_builder.dart'; export 'package:mix/src/widgets/mixable.widget.dart'; export 'package:mix/src/widgets/pressable.widget.dart'; export 'package:mix/src/widgets/text.widget.dart'; @@ -26,3 +26,5 @@ export 'src/theme/mix_theme.dart'; export 'src/theme/refs/color_token.dart'; export 'src/theme/refs/text_style_token.dart'; export 'src/variants/variants.dart'; + + diff --git a/lib/src/attributes/common/attribute.dart b/lib/src/attributes/common/attribute.dart index d07c82b39..f78d6650c 100644 --- a/lib/src/attributes/common/attribute.dart +++ b/lib/src/attributes/common/attribute.dart @@ -7,9 +7,36 @@ import 'package:mix/mix.dart'; // Facade allows us ot set all properties as optional // For improved merge and override of properties abstract class Attribute { + + //final bool isExtension = false; + const Attribute(); } +/// An interface that add support to custom attributes for [MixContext]. +abstract class AttributeExtension> + extends Attribute { + const AttributeExtension(); + + T merge(T other); + + Object get type => T; +} + +class AttributeExtensions { + final Map> extensions; + + AttributeExtensions(this.extensions); + + /// Used to obtain a [AttributeExtension] from [extensions]. + /// + /// Obtain with `mixContext.extension()`. + T? extension() => extensions[T] as T?; + + // TODO(): Add merge method to support inherited extensions +} + + /// Allows to pass down Mixes as attributes for use with helpers class NestedAttribute extends Attribute { const NestedAttribute(this.attributes); diff --git a/lib/src/mixer/mix_context.dart b/lib/src/mixer/mix_context.dart index 5363630c1..0ca00b86a 100644 --- a/lib/src/mixer/mix_context.dart +++ b/lib/src/mixer/mix_context.dart @@ -28,6 +28,7 @@ extension DecoratorMapExtension on DecoratorMap { class MixContext { final BuildContext context; + final AttributeExtensions extensions; final Mix sourceMix; final Mix originalMix; @@ -42,6 +43,11 @@ class MixContext { final FlexProps flexProps; final ZBoxProps zBoxProps; + /// Used to obtain a [AttributeExtension] from [extensions]. + /// + /// Obtain with `mixContext.extension()`. + T extension() => extensions.extension()!; + MixContext._({ required this.context, required this.sourceMix, @@ -55,6 +61,7 @@ class MixContext { required this.directives, required this.variants, required this.decorators, + required this.extensions, }); factory MixContext.create({ @@ -93,6 +100,7 @@ class MixContext { required Mix mix, }) { final _attributes = _expandAttributes(context, mix); + BoxAttributes? boxAttributes; IconAttributes? iconAttributes; FlexAttributes? flexAttributes; @@ -105,7 +113,22 @@ class MixContext { final variants = []; final decorators = []; + final Map extensionAttributes = {}; + for (final attribute in _attributes) { + if (attribute is AttributeExtension) { + var extensionAttribute = extensionAttributes[attribute.type]; + + if (extensionAttribute == null) { + extensionAttribute = attribute; + } else { + extensionAttribute = + extensionAttribute.merge(attribute) as AttributeExtension; + } + + extensionAttributes[attribute.type] = extensionAttribute; + } + if (attribute is VariantAttribute) { variants.add(attribute); } @@ -211,6 +234,7 @@ class MixContext { directives: directives, variants: variants, decorators: decoratorMap, + extensions: AttributeExtensions(extensionAttributes), ); } diff --git a/lib/src/mixer/mix_factory.dart b/lib/src/mixer/mix_factory.dart index 7022ec0f7..34898073e 100644 --- a/lib/src/mixer/mix_factory.dart +++ b/lib/src/mixer/mix_factory.dart @@ -2,16 +2,19 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:mix/mix.dart'; + /// Defines a mix /// {@category Mix Object} class Mix { final List attributes; + final Iterable> extensions; /// These are the `Variants` that will be passed to `MixContext` in order to be applied for this mix final List> variantToApply; const Mix._( this.attributes, { + this.extensions = const [], this.variantToApply = const [], }); @@ -51,6 +54,7 @@ class Mix { const Mix.fromList( this.attributes, { this.variantToApply = const [], + this.extensions = const [], }); /// Instantiate a mix from a _List_ of _Attribute_ instances diff --git a/lib/src/widgets/box.widget.dart b/lib/src/widgets/box.widget.dart index 4b1472203..79163dc48 100644 --- a/lib/src/widgets/box.widget.dart +++ b/lib/src/widgets/box.widget.dart @@ -117,3 +117,4 @@ class BoxMixedWidget extends MixedWidget { return current; } } + diff --git a/lib/src/widgets/mix_context_builder.dart b/lib/src/widgets/mix_builder.dart similarity index 86% rename from lib/src/widgets/mix_context_builder.dart rename to lib/src/widgets/mix_builder.dart index feb4062c8..0ecf96347 100644 --- a/lib/src/widgets/mix_context_builder.dart +++ b/lib/src/widgets/mix_builder.dart @@ -6,10 +6,12 @@ import 'package:mix/src/variants/variants.dart'; import 'package:mix/src/widgets/mixable.widget.dart'; typedef WidgetMixBuilder = Widget Function( - BuildContext context, MixContext mixContext); + BuildContext context, + MixContext mixContext, +); -class MixContextBuilder extends MixableWidget { - const MixContextBuilder({ +class MixBuilder extends MixableWidget { + const MixBuilder({ required this.builder, Mix? mix, bool? inherit, @@ -27,6 +29,7 @@ class MixContextBuilder extends MixableWidget { @override Widget build(BuildContext context) { final mixContext = getMixContext(context); + return MixContextNotifier( mixContext, child: builder( diff --git a/lib/src/widgets/mixable.widget.dart b/lib/src/widgets/mixable.widget.dart index 25ac024a7..fb013be7b 100644 --- a/lib/src/widgets/mixable.widget.dart +++ b/lib/src/widgets/mixable.widget.dart @@ -9,6 +9,60 @@ import '../attributes/shared/shared.props.dart'; import '../attributes/text/text.props.dart'; import '../attributes/zbox/zbox.props.dart'; +/// Mix Widget +class MixableBuilder extends StatelessWidget { + final Widget Function(MixContext mixContext) builder; + + /// Constructor + const MixableBuilder({ + Key? key, + bool? inherit, + List? variants, + required this.builder, + required Mix mix, + }) : _mix = mix, + _variants = variants, + _inherit = inherit ?? true, + super(key: key); + + final Mix _mix; + + final List? _variants; + final bool _inherit; + + MixContext getMixContext(BuildContext context) { + return MixContext.create( + context: context, + mix: _mix.withMaybeVariants(_variants), + inherit: _inherit, + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + + properties.add( + DiagnosticsProperty('mix', _mix, defaultValue: null), + ); + properties.add( + DiagnosticsProperty>( + 'variants', + _variants, + defaultValue: null, + ), + ); + properties.add( + DiagnosticsProperty('inherit', _inherit, defaultValue: true), + ); + } + + @override + Widget build(BuildContext context) { + return builder(getMixContext(context)); + } +} + /// Mix Widget abstract class MixableWidget extends StatelessWidget { /// Constructor @@ -46,8 +100,11 @@ abstract class MixableWidget extends StatelessWidget { DiagnosticsProperty('mix', _mix, defaultValue: null), ); properties.add( - DiagnosticsProperty>('variants', _variants, - defaultValue: null), + DiagnosticsProperty>( + 'variants', + _variants, + defaultValue: null, + ), ); properties.add( DiagnosticsProperty('inherit', _inherit, defaultValue: true), diff --git a/test/widgets/extension_test.dart b/test/widgets/extension_test.dart new file mode 100644 index 000000000..48bdb51ee --- /dev/null +++ b/test/widgets/extension_test.dart @@ -0,0 +1,254 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mix/mix.dart' hide border, enabled, icon, iconColor; + +import '../testing_utils.dart'; + +final activated = Variant("activated"); + +const myIcon = MyIconAttributes.icon; +const withSize = MyIconAttributes.withSize; +const withColor = MyIconAttributes.withColor; + +const inputDecoration = InputDecorationAttributes.inputDecoration; + +class MyIconAttributes extends AttributeExtension { + const MyIconAttributes({ + this.color, + this.size, + }); + + final Color? color; + final double? size; + + @override + MyIconAttributes merge(MyIconAttributes other) { + return MyIconAttributes( + color: other.color ?? color, + size: other.size ?? size, + ); + } + + static MyIconAttributes icon({required Color color, required double size}) { + return MyIconAttributes( + size: size, + color: color, + ); + } + + static MyIconAttributes withColor(Color color) { + return MyIconAttributes( + color: color, + ); + } + + static MyIconAttributes withSize(double size) { + return MyIconAttributes( + size: size, + ); + } +} + +class InputDecorationAttributes extends InputDecoration + implements AttributeExtension { + const InputDecorationAttributes({ + Color? iconColor, + Color? fillColor, + InputBorder? border, + }) : super(iconColor: iconColor, fillColor: fillColor, border: border); + + @override + InputDecorationAttributes merge(InputDecorationAttributes other) { + return InputDecorationAttributes( + iconColor: other.iconColor ?? iconColor, + fillColor: other.fillColor ?? fillColor, + border: other.border ?? border, + ); + } + + static InputDecorationAttributes inputDecoration({ + Color? iconColor, + Color? fillColor, + InputBorder? border, + }) { + return InputDecorationAttributes( + iconColor: iconColor ?? iconColor, + fillColor: fillColor ?? fillColor, + border: border ?? border, + ); + } + + @override + Object get type => InputDecorationAttributes; +} + +class Style { + late final base = Mix( + withSize(23), + withColor(Colors.green), + inputDecoration( + border: const UnderlineInputBorder( + borderSide: BorderSide(color: Colors.red, width: 2), + ), + ), + inputDecoration( + fillColor: Colors.red, + ), + activated(withColor(Colors.blue), inputDecoration(fillColor: Colors.green)), + const SharedAttributes(textDirection: TextDirection.rtl), + ); +} + +class CustomWidget extends StatelessWidget { + const CustomWidget( + this.icon, { + this.semanticLabel, + Key? key, + this.variants = const [], + }) : super( + key: key, + ); + + final IconData? icon; + final String? semanticLabel; + final List variants; + + @override + Widget build(BuildContext context) { + return MixableBuilder( + variants: variants, + builder: (context) { + final attributes = context.extension(); + + return Semantics( + label: semanticLabel, + child: Column( + children: [ + Icon( + icon, + color: attributes.color, + size: attributes.size, + textDirection: context.sharedProps.textDirection, + ), + ], + ), + ); + }, + mix: Style().base, + ); + } +} + +class TextFieldWidget extends StatelessWidget { + const TextFieldWidget( + this.icon, { + this.semanticLabel, + Key? key, + this.variants = const [], + }) : super( + key: key, + ); + + final IconData? icon; + final String? semanticLabel; + final List variants; + + @override + Widget build(BuildContext context) { + return MixableBuilder( + mix: Style().base, + variants: variants, + builder: (context) { + final inputDecoration = context.extension(); + + return Semantics( + label: semanticLabel, + child: Column( + children: [ + TextField( + decoration: inputDecoration, + ), + ], + ), + ); + }, + ); + } +} + +void main() { + group("mix attribute extension", () { + testWidgets('custom icon mix attribute extension', (tester) async { + await tester.pumpWidget( + const MixTestWidget( + child: CustomWidget(Icons.bolt), + ), + ); + + final icon = tester.widget(find.byType(Icon)); + + expect(icon.color, Colors.green); + expect(icon.size, 23); + expect(icon.icon, Icons.bolt); + + expect(icon.textDirection, TextDirection.rtl); + }); + + testWidgets('TextField', (tester) async { + await tester.pumpWidget( + MixTestWidget( + child: CustomWidget( + Icons.bolt, + variants: [activated], + ), + ), + ); + + final icon = tester.widget(find.byType(Icon)); + + expect(icon.color, Colors.blue); + expect(icon.size, 23); + expect(icon.icon, Icons.bolt); + + expect(icon.textDirection, TextDirection.rtl); + }); + }); + + group("TextField attribute extension with variant", () { + testWidgets('custom icon mix attribute extension', (tester) async { + await tester.pumpWidget( + const MixTestWidget( + child: MaterialApp( + home: Material( + child: TextFieldWidget( + Icons.bolt, + variants: [], + ), + ), + ), + ), + ); + + final textField = tester.widget(find.byType(TextField)); + expect(textField.decoration!.fillColor, Colors.red); + }); + + testWidgets('TextField', (tester) async { + await tester.pumpWidget( + MixTestWidget( + child: MaterialApp( + home: Scaffold( + body: TextFieldWidget( + Icons.bolt, + variants: [activated], + ), + ), + ), + ), + ); + + final textField = tester.widget(find.byType(TextField)); + expect(textField.decoration!.fillColor, Colors.green); + }); + }); +} From 417349f7e0e0cf4affd519591cda4419343513c6 Mon Sep 17 00:00:00 2001 From: Pedro Bissonho Date: Mon, 26 Sep 2022 19:38:46 -0300 Subject: [PATCH 2/9] refactor --- lib/src/attributes/common/attribute.dart | 24 +++++++---------- lib/src/mixer/mix_context.dart | 27 +++++++++---------- lib/src/mixer/mix_factory.dart | 5 +--- .../inherited_attribute_test.dart} | 25 ++++++++++------- 4 files changed, 39 insertions(+), 42 deletions(-) rename test/{widgets/extension_test.dart => attributes/inherited_attribute_test.dart} (90%) diff --git a/lib/src/attributes/common/attribute.dart b/lib/src/attributes/common/attribute.dart index f78d6650c..6acf7aa97 100644 --- a/lib/src/attributes/common/attribute.dart +++ b/lib/src/attributes/common/attribute.dart @@ -7,36 +7,32 @@ import 'package:mix/mix.dart'; // Facade allows us ot set all properties as optional // For improved merge and override of properties abstract class Attribute { - //final bool isExtension = false; - + const Attribute(); } /// An interface that add support to custom attributes for [MixContext]. -abstract class AttributeExtension> +abstract class InheritedAttribute> extends Attribute { - const AttributeExtension(); + const InheritedAttribute(); - T merge(T other); + InheritedAttribute merge(covariant InheritedAttribute? other); Object get type => T; } -class AttributeExtensions { - final Map> extensions; +class InheritedAttributes { + final Map> attributes; - AttributeExtensions(this.extensions); + InheritedAttributes(this.attributes); - /// Used to obtain a [AttributeExtension] from [extensions]. + /// Used to obtain a [InheritedAttribute] from [MixContext]. /// - /// Obtain with `mixContext.extension()`. - T? extension() => extensions[T] as T?; - - // TODO(): Add merge method to support inherited extensions + /// Obtain with `mixContext.fromType()`. + T? fromType() => attributes[T] as T?; } - /// Allows to pass down Mixes as attributes for use with helpers class NestedAttribute extends Attribute { const NestedAttribute(this.attributes); diff --git a/lib/src/mixer/mix_context.dart b/lib/src/mixer/mix_context.dart index 0ca00b86a..18c87e829 100644 --- a/lib/src/mixer/mix_context.dart +++ b/lib/src/mixer/mix_context.dart @@ -28,7 +28,7 @@ extension DecoratorMapExtension on DecoratorMap { class MixContext { final BuildContext context; - final AttributeExtensions extensions; + final InheritedAttributes inheritedAttributes; final Mix sourceMix; final Mix originalMix; @@ -43,10 +43,10 @@ class MixContext { final FlexProps flexProps; final ZBoxProps zBoxProps; - /// Used to obtain a [AttributeExtension] from [extensions]. + /// Used to obtain a [InheritedAttribute] from [InheritedAttributes]. /// - /// Obtain with `mixContext.extension()`. - T extension() => extensions.extension()!; + /// Obtain with `mixContext.fromType()`. + T fromType() => inheritedAttributes.fromType()!; MixContext._({ required this.context, @@ -61,7 +61,7 @@ class MixContext { required this.directives, required this.variants, required this.decorators, - required this.extensions, + required this.inheritedAttributes, }); factory MixContext.create({ @@ -113,20 +113,19 @@ class MixContext { final variants = []; final decorators = []; - final Map extensionAttributes = {}; + final Map inheritedAttributesMap = {}; for (final attribute in _attributes) { - if (attribute is AttributeExtension) { - var extensionAttribute = extensionAttributes[attribute.type]; + if (attribute is InheritedAttribute) { + var inheritedAttribute = inheritedAttributesMap[attribute.type]; - if (extensionAttribute == null) { - extensionAttribute = attribute; + if (inheritedAttribute == null) { + inheritedAttribute = attribute; } else { - extensionAttribute = - extensionAttribute.merge(attribute) as AttributeExtension; + inheritedAttribute = inheritedAttribute.merge(attribute); } - extensionAttributes[attribute.type] = extensionAttribute; + inheritedAttributesMap[attribute.type] = inheritedAttribute; } if (attribute is VariantAttribute) { @@ -234,7 +233,7 @@ class MixContext { directives: directives, variants: variants, decorators: decoratorMap, - extensions: AttributeExtensions(extensionAttributes), + inheritedAttributes: InheritedAttributes(inheritedAttributesMap), ); } diff --git a/lib/src/mixer/mix_factory.dart b/lib/src/mixer/mix_factory.dart index 34898073e..f50ff3fb8 100644 --- a/lib/src/mixer/mix_factory.dart +++ b/lib/src/mixer/mix_factory.dart @@ -2,19 +2,16 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:mix/mix.dart'; - /// Defines a mix /// {@category Mix Object} class Mix { final List attributes; - final Iterable> extensions; /// These are the `Variants` that will be passed to `MixContext` in order to be applied for this mix final List> variantToApply; const Mix._( this.attributes, { - this.extensions = const [], this.variantToApply = const [], }); @@ -54,7 +51,6 @@ class Mix { const Mix.fromList( this.attributes, { this.variantToApply = const [], - this.extensions = const [], }); /// Instantiate a mix from a _List_ of _Attribute_ instances @@ -123,6 +119,7 @@ class Mix { } /// Merges many mixes into one + // ignore: long-parameter-list static Mix combine([ Mix? mix1, Mix? mix2, diff --git a/test/widgets/extension_test.dart b/test/attributes/inherited_attribute_test.dart similarity index 90% rename from test/widgets/extension_test.dart rename to test/attributes/inherited_attribute_test.dart index 48bdb51ee..5784320ab 100644 --- a/test/widgets/extension_test.dart +++ b/test/attributes/inherited_attribute_test.dart @@ -12,7 +12,7 @@ const withColor = MyIconAttributes.withColor; const inputDecoration = InputDecorationAttributes.inputDecoration; -class MyIconAttributes extends AttributeExtension { +class MyIconAttributes extends InheritedAttribute { const MyIconAttributes({ this.color, this.size, @@ -22,10 +22,14 @@ class MyIconAttributes extends AttributeExtension { final double? size; @override - MyIconAttributes merge(MyIconAttributes other) { + MyIconAttributes merge(MyIconAttributes? other) { + if(other is! InheritedAttribute) { + return this; + } + return MyIconAttributes( - color: other.color ?? color, - size: other.size ?? size, + color: other?.color ?? color, + size: other?.size ?? size, ); } @@ -50,7 +54,7 @@ class MyIconAttributes extends AttributeExtension { } class InputDecorationAttributes extends InputDecoration - implements AttributeExtension { + implements InheritedAttribute { const InputDecorationAttributes({ Color? iconColor, Color? fillColor, @@ -117,8 +121,8 @@ class CustomWidget extends StatelessWidget { Widget build(BuildContext context) { return MixableBuilder( variants: variants, - builder: (context) { - final attributes = context.extension(); + builder: (mixContext) { + final attributes = mixContext.fromType(); return Semantics( label: semanticLabel, @@ -128,7 +132,7 @@ class CustomWidget extends StatelessWidget { icon, color: attributes.color, size: attributes.size, - textDirection: context.sharedProps.textDirection, + textDirection: mixContext.sharedProps.textDirection, ), ], ), @@ -158,8 +162,9 @@ class TextFieldWidget extends StatelessWidget { return MixableBuilder( mix: Style().base, variants: variants, - builder: (context) { - final inputDecoration = context.extension(); + builder: (mixContext) { + final inputDecoration = + mixContext.fromType(); return Semantics( label: semanticLabel, From cae376d3a105dc1b0a01f76e40160a43e06e1bee Mon Sep 17 00:00:00 2001 From: Pedro Bissonho Date: Mon, 26 Sep 2022 19:45:54 -0300 Subject: [PATCH 3/9] refactor --- lib/src/widgets/mix_builder.dart | 5 +- lib/src/widgets/mixable.widget.dart | 54 ------------------- test/attributes/inherited_attribute_test.dart | 10 ++-- 3 files changed, 8 insertions(+), 61 deletions(-) diff --git a/lib/src/widgets/mix_builder.dart b/lib/src/widgets/mix_builder.dart index 0ecf96347..ee8789b37 100644 --- a/lib/src/widgets/mix_builder.dart +++ b/lib/src/widgets/mix_builder.dart @@ -10,8 +10,8 @@ typedef WidgetMixBuilder = Widget Function( MixContext mixContext, ); -class MixBuilder extends MixableWidget { - const MixBuilder({ +class MixContextBuilder extends MixableWidget { + const MixContextBuilder({ required this.builder, Mix? mix, bool? inherit, @@ -39,3 +39,4 @@ class MixBuilder extends MixableWidget { ); } } + diff --git a/lib/src/widgets/mixable.widget.dart b/lib/src/widgets/mixable.widget.dart index fb013be7b..8cf91b89a 100644 --- a/lib/src/widgets/mixable.widget.dart +++ b/lib/src/widgets/mixable.widget.dart @@ -9,60 +9,6 @@ import '../attributes/shared/shared.props.dart'; import '../attributes/text/text.props.dart'; import '../attributes/zbox/zbox.props.dart'; -/// Mix Widget -class MixableBuilder extends StatelessWidget { - final Widget Function(MixContext mixContext) builder; - - /// Constructor - const MixableBuilder({ - Key? key, - bool? inherit, - List? variants, - required this.builder, - required Mix mix, - }) : _mix = mix, - _variants = variants, - _inherit = inherit ?? true, - super(key: key); - - final Mix _mix; - - final List? _variants; - final bool _inherit; - - MixContext getMixContext(BuildContext context) { - return MixContext.create( - context: context, - mix: _mix.withMaybeVariants(_variants), - inherit: _inherit, - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - - properties.add( - DiagnosticsProperty('mix', _mix, defaultValue: null), - ); - properties.add( - DiagnosticsProperty>( - 'variants', - _variants, - defaultValue: null, - ), - ); - properties.add( - DiagnosticsProperty('inherit', _inherit, defaultValue: true), - ); - } - - @override - Widget build(BuildContext context) { - return builder(getMixContext(context)); - } -} - /// Mix Widget abstract class MixableWidget extends StatelessWidget { /// Constructor diff --git a/test/attributes/inherited_attribute_test.dart b/test/attributes/inherited_attribute_test.dart index 5784320ab..de55d8767 100644 --- a/test/attributes/inherited_attribute_test.dart +++ b/test/attributes/inherited_attribute_test.dart @@ -23,7 +23,7 @@ class MyIconAttributes extends InheritedAttribute { @override MyIconAttributes merge(MyIconAttributes? other) { - if(other is! InheritedAttribute) { + if (other is! InheritedAttribute) { return this; } @@ -119,9 +119,9 @@ class CustomWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return MixableBuilder( + return MixContextBuilder( variants: variants, - builder: (mixContext) { + builder: (context, mixContext) { final attributes = mixContext.fromType(); return Semantics( @@ -159,10 +159,10 @@ class TextFieldWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return MixableBuilder( + return MixContextBuilder( mix: Style().base, variants: variants, - builder: (mixContext) { + builder: (context, mixContext) { final inputDecoration = mixContext.fromType(); From 92e47b551414ac7083c4c4eae6ed839015a3a6a2 Mon Sep 17 00:00:00 2001 From: Pedro Bissonho Date: Mon, 26 Sep 2022 19:47:06 -0300 Subject: [PATCH 4/9] refactor --- lib/src/widgets/{mix_builder.dart => mix_context_builder.dart} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/src/widgets/{mix_builder.dart => mix_context_builder.dart} (100%) diff --git a/lib/src/widgets/mix_builder.dart b/lib/src/widgets/mix_context_builder.dart similarity index 100% rename from lib/src/widgets/mix_builder.dart rename to lib/src/widgets/mix_context_builder.dart From a189ae284891e999b30c589902fe6ad945f80aaf Mon Sep 17 00:00:00 2001 From: Pedro Bissonho Date: Mon, 26 Sep 2022 20:41:43 -0300 Subject: [PATCH 5/9] refactor: remove comment --- lib/src/attributes/common/attribute.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/src/attributes/common/attribute.dart b/lib/src/attributes/common/attribute.dart index 6acf7aa97..806ffbf4f 100644 --- a/lib/src/attributes/common/attribute.dart +++ b/lib/src/attributes/common/attribute.dart @@ -7,8 +7,6 @@ import 'package:mix/mix.dart'; // Facade allows us ot set all properties as optional // For improved merge and override of properties abstract class Attribute { - //final bool isExtension = false; - const Attribute(); } From a4d70e1c4d57a6d9d4e0d1ffec41ecc3f868a783 Mon Sep 17 00:00:00 2001 From: Pedro Bissonho Date: Tue, 27 Sep 2022 08:21:31 -0300 Subject: [PATCH 6/9] fix export --- lib/mix.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mix.dart b/lib/mix.dart index 04ddf9205..0607566f2 100644 --- a/lib/mix.dart +++ b/lib/mix.dart @@ -5,7 +5,7 @@ export 'package:mix/src/widgets/box.widget.dart'; export 'package:mix/src/widgets/flex.widget.dart'; export 'package:mix/src/widgets/gap.widget.dart'; export 'package:mix/src/widgets/icon.widget.dart'; -export 'package:mix/src/widgets/mix_builder.dart'; +export 'package:mix/src/widgets/mix_context_builder.dart'; export 'package:mix/src/widgets/mixable.widget.dart'; export 'package:mix/src/widgets/pressable.widget.dart'; export 'package:mix/src/widgets/text.widget.dart'; From 1ba06e534e673b5ce24ef9da2e1906085af21cd4 Mon Sep 17 00:00:00 2001 From: Pedro Bissonho Date: Tue, 27 Sep 2022 08:36:40 -0300 Subject: [PATCH 7/9] refactor: tests --- test/attributes/inherited_attribute_test.dart | 119 +++++++++--------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/test/attributes/inherited_attribute_test.dart b/test/attributes/inherited_attribute_test.dart index de55d8767..0a5489718 100644 --- a/test/attributes/inherited_attribute_test.dart +++ b/test/attributes/inherited_attribute_test.dart @@ -6,14 +6,15 @@ import '../testing_utils.dart'; final activated = Variant("activated"); -const myIcon = MyIconAttributes.icon; -const withSize = MyIconAttributes.withSize; -const withColor = MyIconAttributes.withColor; +const customIcon = InheritedIconAttribute.icon; +const withSize = InheritedIconAttribute.withSize; +const withColor = InheritedIconAttribute.withColor; -const inputDecoration = InputDecorationAttributes.inputDecoration; +const inputDecoration = InputDecorationThemeAttribute.inputDecoration; -class MyIconAttributes extends InheritedAttribute { - const MyIconAttributes({ +class InheritedIconAttribute + extends InheritedAttribute { + const InheritedIconAttribute({ this.color, this.size, }); @@ -22,60 +23,63 @@ class MyIconAttributes extends InheritedAttribute { final double? size; @override - MyIconAttributes merge(MyIconAttributes? other) { - if (other is! InheritedAttribute) { - return this; - } - - return MyIconAttributes( - color: other?.color ?? color, - size: other?.size ?? size, + InheritedIconAttribute merge(InheritedIconAttribute other) { + return InheritedIconAttribute( + color: other.color ?? color, + size: other.size ?? size, ); } - static MyIconAttributes icon({required Color color, required double size}) { - return MyIconAttributes( + static InheritedIconAttribute icon({ + required Color color, + required double size, + }) { + return InheritedIconAttribute( size: size, color: color, ); } - static MyIconAttributes withColor(Color color) { - return MyIconAttributes( + static InheritedIconAttribute withColor(Color color) { + return InheritedIconAttribute( color: color, ); } - static MyIconAttributes withSize(double size) { - return MyIconAttributes( + static InheritedIconAttribute withSize(double size) { + return InheritedIconAttribute( size: size, ); } } -class InputDecorationAttributes extends InputDecoration - implements InheritedAttribute { - const InputDecorationAttributes({ +class InputDecorationThemeAttribute extends InputDecorationTheme + implements InheritedAttribute { + const InputDecorationThemeAttribute({ Color? iconColor, Color? fillColor, InputBorder? border, - }) : super(iconColor: iconColor, fillColor: fillColor, border: border); + }) : super( + iconColor: iconColor, + fillColor: fillColor, + border: border, + ); @override - InputDecorationAttributes merge(InputDecorationAttributes other) { - return InputDecorationAttributes( + InputDecorationThemeAttribute merge(InputDecorationThemeAttribute other) { + return InputDecorationThemeAttribute( iconColor: other.iconColor ?? iconColor, fillColor: other.fillColor ?? fillColor, border: other.border ?? border, ); } - static InputDecorationAttributes inputDecoration({ + static InputDecorationThemeAttribute inputDecoration({ Color? iconColor, Color? fillColor, InputBorder? border, }) { - return InputDecorationAttributes( + return InputDecorationThemeAttribute( iconColor: iconColor ?? iconColor, fillColor: fillColor ?? fillColor, border: border ?? border, @@ -83,25 +87,23 @@ class InputDecorationAttributes extends InputDecoration } @override - Object get type => InputDecorationAttributes; + Object get type => InputDecorationThemeAttribute; } -class Style { - late final base = Mix( - withSize(23), - withColor(Colors.green), - inputDecoration( - border: const UnderlineInputBorder( - borderSide: BorderSide(color: Colors.red, width: 2), - ), +final mix = Mix( + withSize(23), + withColor(Colors.green), + inputDecoration( + border: const UnderlineInputBorder( + borderSide: BorderSide(color: Colors.red, width: 2), ), - inputDecoration( - fillColor: Colors.red, - ), - activated(withColor(Colors.blue), inputDecoration(fillColor: Colors.green)), - const SharedAttributes(textDirection: TextDirection.rtl), - ); -} + ), + inputDecoration( + fillColor: Colors.red, + ), + activated(withColor(Colors.blue), inputDecoration(fillColor: Colors.green)), + const SharedAttributes(textDirection: TextDirection.rtl), +); class CustomWidget extends StatelessWidget { const CustomWidget( @@ -122,7 +124,7 @@ class CustomWidget extends StatelessWidget { return MixContextBuilder( variants: variants, builder: (context, mixContext) { - final attributes = mixContext.fromType(); + final attribute = mixContext.fromType(); return Semantics( label: semanticLabel, @@ -130,15 +132,15 @@ class CustomWidget extends StatelessWidget { children: [ Icon( icon, - color: attributes.color, - size: attributes.size, + color: attribute.color, + size: attribute.size, textDirection: mixContext.sharedProps.textDirection, ), ], ), ); }, - mix: Style().base, + mix: mix, ); } } @@ -160,18 +162,19 @@ class TextFieldWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MixContextBuilder( - mix: Style().base, + mix: mix, variants: variants, builder: (context, mixContext) { - final inputDecoration = - mixContext.fromType(); + final decorationTheme = + mixContext.fromType(); return Semantics( label: semanticLabel, child: Column( children: [ TextField( - decoration: inputDecoration, + decoration: + const InputDecoration().applyDefaults(decorationTheme), ), ], ), @@ -182,8 +185,8 @@ class TextFieldWidget extends StatelessWidget { } void main() { - group("mix attribute extension", () { - testWidgets('custom icon mix attribute extension', (tester) async { + group("inherited icon attribute", () { + testWidgets('without variants', (tester) async { await tester.pumpWidget( const MixTestWidget( child: CustomWidget(Icons.bolt), @@ -199,7 +202,7 @@ void main() { expect(icon.textDirection, TextDirection.rtl); }); - testWidgets('TextField', (tester) async { + testWidgets('with variant', (tester) async { await tester.pumpWidget( MixTestWidget( child: CustomWidget( @@ -219,8 +222,8 @@ void main() { }); }); - group("TextField attribute extension with variant", () { - testWidgets('custom icon mix attribute extension', (tester) async { + group("inherited input theme attribute", () { + testWidgets('without variants', (tester) async { await tester.pumpWidget( const MixTestWidget( child: MaterialApp( @@ -238,7 +241,7 @@ void main() { expect(textField.decoration!.fillColor, Colors.red); }); - testWidgets('TextField', (tester) async { + testWidgets('with variants', (tester) async { await tester.pumpWidget( MixTestWidget( child: MaterialApp( From 2f35d3fde696ccc9e7fcd07d7571e41915c56ffe Mon Sep 17 00:00:00 2001 From: Pedro Bissonho Date: Tue, 27 Sep 2022 09:13:00 -0300 Subject: [PATCH 8/9] add debug check --- lib/src/mixer/mix_context.dart | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/src/mixer/mix_context.dart b/lib/src/mixer/mix_context.dart index 18c87e829..cb1ad6a9e 100644 --- a/lib/src/mixer/mix_context.dart +++ b/lib/src/mixer/mix_context.dart @@ -46,7 +46,12 @@ class MixContext { /// Used to obtain a [InheritedAttribute] from [InheritedAttributes]. /// /// Obtain with `mixContext.fromType()`. - T fromType() => inheritedAttributes.fromType()!; + T fromType>() { + final attribute = inheritedAttributes.fromType(); + debugCheckInheritedAttribute(attribute); + + return attribute!; + } MixContext._({ required this.context, @@ -358,3 +363,17 @@ class MixContext { // ); // } } + +/// Asserts that the given mixContext has a [T] attribute. +/// Does nothing if asserts are disabled. Always returns true. +void debugCheckInheritedAttribute>( + InheritedAttribute? attribute, +) { + assert(() { + return attribute is T; + }(), ''' + No $T could be found starting from the mixContext + when call mixContext.fromType<$T>(). This can happen because you + have not create a Mix with $T. + '''); +} From fb25631fdea217df27805773be3069792217fe7d Mon Sep 17 00:00:00 2001 From: Pedro Bissonho Date: Tue, 27 Sep 2022 09:14:26 -0300 Subject: [PATCH 9/9] refactor --- lib/src/mixer/mix_context.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/mixer/mix_context.dart b/lib/src/mixer/mix_context.dart index cb1ad6a9e..4c8d2790c 100644 --- a/lib/src/mixer/mix_context.dart +++ b/lib/src/mixer/mix_context.dart @@ -365,7 +365,6 @@ class MixContext { } /// Asserts that the given mixContext has a [T] attribute. -/// Does nothing if asserts are disabled. Always returns true. void debugCheckInheritedAttribute>( InheritedAttribute? attribute, ) {