diff --git a/lib/src/core/attribute.dart b/lib/src/core/attribute.dart index a40324a93..450683d09 100644 --- a/lib/src/core/attribute.dart +++ b/lib/src/core/attribute.dart @@ -9,6 +9,9 @@ abstract class Attribute with Comparable { // Used as a merge type Object get type; + + // Used to determine if the attribute is inheritable + bool get isInheritable => true; } @immutable diff --git a/lib/src/core/decorator.dart b/lib/src/core/decorator.dart index 2289c6fc6..ce9ebe831 100644 --- a/lib/src/core/decorator.dart +++ b/lib/src/core/decorator.dart @@ -13,6 +13,9 @@ abstract class Decorator> extends StyleAttribute { @override Object get type => Self; + @override + bool get isInheritable => false; + Widget build(MixData mix, Widget child); } diff --git a/lib/src/core/styled_widget.dart b/lib/src/core/styled_widget.dart index ccd59fafa..90bb54018 100644 --- a/lib/src/core/styled_widget.dart +++ b/lib/src/core/styled_widget.dart @@ -3,8 +3,6 @@ import 'package:flutter/material.dart'; import '../factory/mix_provider.dart'; import '../factory/mix_provider_data.dart'; import '../factory/style_mix.dart'; -import 'attribute.dart'; -import 'decorator.dart'; /// An abstract widget for applying custom styles. /// @@ -39,11 +37,7 @@ abstract class StyledWidget extends StatelessWidget { /// This method is typically used in the `build` method of widgets extending /// [StyledWidget] to provide the actual styled widget. Widget withMix(BuildContext context, MixBuilder builder) { - MixData? inheritedMix = inherit ? MixProvider.maybeOf(context) : null; - - if (inheritedMix != null) { - inheritedMix = MixData.where(inheritedMix, _handleDecoratorsOnInhirit); - } + MixData? inheritedMix = inherit ? MixData.inherited(context) : null; final mixData = MixData.create(context, style); @@ -52,8 +46,6 @@ abstract class StyledWidget extends StatelessWidget { return MixProvider(data: mergedMixData, child: builder(mergedMixData)); } - bool _handleDecoratorsOnInhirit(Attribute attr) => attr is! WidgetDecorator; - @override Widget build(BuildContext context); } diff --git a/lib/src/factory/mix_provider_data.dart b/lib/src/factory/mix_provider_data.dart index 36bea49a0..f9d7c1977 100644 --- a/lib/src/factory/mix_provider_data.dart +++ b/lib/src/factory/mix_provider_data.dart @@ -6,6 +6,7 @@ import '../core/attributes_map.dart'; import '../helpers/compare_mixin.dart'; import '../theme/mix_theme.dart'; import '../widgets/pressable/widget_state_util.dart'; +import 'mix_provider.dart'; import 'style_mix.dart'; /// This class is used for encapsulating all [MixData] related operations. @@ -50,6 +51,24 @@ class MixData with Comparable { ); } + factory MixData.inherited(BuildContext context) { + final inheritedMix = MixProvider.maybeOf(context); + + if (inheritedMix == null) { + return MixData.create(context, const Style.empty()); + } + + // Remove non-inheritable attributes + final inheritableAttributes = inheritedMix.where( + (attr) => attr.isInheritable, + ); + + return MixData._( + resolver: MixTokenResolver(context), + attributes: AttributeMap(inheritableAttributes), + ); + } + /// Getter for [MixTokenResolver]. /// /// Returns [_tokenResolver]. @@ -73,6 +92,10 @@ class MixData with Comparable { return _attributes.whereType(); } + bool contains() { + return _attributes.values.any((attr) => attr is T); + } + Iterable where(bool Function(Attribute attr) clousure) => attributes.values.where(clousure); diff --git a/lib/src/specs/container/box_widget.dart b/lib/src/specs/container/box_widget.dart index fa1bc99f3..101cc08bf 100644 --- a/lib/src/specs/container/box_widget.dart +++ b/lib/src/specs/container/box_widget.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import '../../core/styled_widget.dart'; -import '../../decorators/widget_decorator_widget.dart'; import '../../deprecations.dart'; import '../../factory/mix_provider.dart'; import '../../factory/mix_provider_data.dart'; +import '../../utils/helper_util.dart'; import 'box_attribute.dart'; import 'box_spec.dart'; @@ -115,7 +115,7 @@ class MixedBox extends StatelessWidget { final spec = BoxSpec.of(mix); // Apply styles and decorators to the Container, which wraps the child widget. - return RenderWidgetDecorators( + return shouldApplyDecorators( mix: mix, orderOfDecorators: decoratorOrder, child: Container( diff --git a/lib/src/specs/icon/icon_widget.dart b/lib/src/specs/icon/icon_widget.dart index 748212290..f4c5bf4fb 100644 --- a/lib/src/specs/icon/icon_widget.dart +++ b/lib/src/specs/icon/icon_widget.dart @@ -1,11 +1,6 @@ import 'package:flutter/material.dart'; -import '../../core/styled_widget.dart'; -import '../../decorators/widget_decorator_widget.dart'; -import '../../factory/mix_provider.dart'; -import '../../factory/mix_provider_data.dart'; -import 'icon_attribute.dart'; -import 'icon_spec.dart'; +import '../../../mix.dart'; class StyledIcon extends StyledWidget { const StyledIcon( @@ -54,8 +49,9 @@ class MixedIcon extends StatelessWidget { final mix = this.mix ?? MixProvider.of(context); final spec = IconSpec.of(mix); - return RenderWidgetDecorators( + return shouldApplyDecorators( mix: mix, + orderOfDecorators: decoratorOrder, child: IconSpecWidget( spec: spec, semanticLabel: semanticLabel, diff --git a/lib/src/utils/helper_util.dart b/lib/src/utils/helper_util.dart index 199279016..f762a9484 100644 --- a/lib/src/utils/helper_util.dart +++ b/lib/src/utils/helper_util.dart @@ -1,3 +1,9 @@ +import 'package:flutter/widgets.dart'; + +import '../core/decorator.dart'; +import '../decorators/widget_decorator_widget.dart'; +import '../factory/mix_provider_data.dart'; + typedef FunctionWithParams = ReturnT Function( List params, ); @@ -35,3 +41,17 @@ class SpreadFunctionParams { ].whereType().toList()); } } + +Widget shouldApplyDecorators({ + required MixData mix, + required Widget child, + List orderOfDecorators = const [], +}) { + return mix.contains() + ? RenderWidgetDecorators( + mix: mix, + orderOfDecorators: orderOfDecorators, + child: child, + ) + : child; +} diff --git a/test/src/decorators/widget_decorator_widget_test.dart b/test/src/decorators/widget_decorator_widget_test.dart index 92605ffb6..9e5205371 100644 --- a/test/src/decorators/widget_decorator_widget_test.dart +++ b/test/src/decorators/widget_decorator_widget_test.dart @@ -153,7 +153,7 @@ void main() { testWidgets('Renders decorators in the correct order', (tester) async { await tester.pumpMaterialApp( RenderWidgetDecorators( - mix: MixData.create(MockBuildContext(), style), + mix: mixData, orderOfDecorators: const [ ClipDecorator, AspectRatioDecorator, @@ -287,5 +287,30 @@ void main() { ), findsNothing); }); + + testWidgets( + 'If there are no decorator attributes in Style, RenderWidgetDecorators shouldnt exist in the widget tree', + (tester) async { + const key = Key('box'); + + await tester.pumpWidget( + Box( + key: key, + style: Style( + backgroundColor.red(), + height(100), + width(100), + ), + ), + ); + + expect( + find.descendant( + of: find.byKey(key), + matching: find.byType(RenderWidgetDecorators), + ), + findsNothing, + ); + }); }); } diff --git a/test/src/factory/mix_provider_data_test.dart b/test/src/factory/mix_provider_data_test.dart index bf39c31a3..2dc83129c 100644 --- a/test/src/factory/mix_provider_data_test.dart +++ b/test/src/factory/mix_provider_data_test.dart @@ -1,9 +1,6 @@ +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mix/src/core/attributes_map.dart'; -import 'package:mix/src/factory/mix_provider_data.dart'; -import 'package:mix/src/factory/style_mix.dart'; -import 'package:mix/src/theme/mix_theme.dart'; -import 'package:mix/src/variants/variant.dart'; +import 'package:mix/mix.dart'; import '../../helpers/testing_utils.dart'; @@ -86,4 +83,35 @@ void main() { expect(mergedMixData.attributeOf(), const MockDoubleScalarAttribute(4.0)); }); + + testWidgets('MixData.inherited shouldnt have attributes non inheritable', + (WidgetTester tester) async { + await tester.pumpWidget( + MixProvider( + data: MixData.create( + MockBuildContext(), + Style( + const _NonInheritableAttribute(), + icon.color.black(), + ), + ), + child: Builder(builder: (context) { + final inheritedMix = MixData.inherited(context); + final iconSpec = IconSpec.of(inheritedMix); + + expect(inheritedMix.attributes.length, 1); + expect(iconSpec.color, Colors.black); + return const SizedBox(); + }), + ), + ); + }); +} + +class _NonInheritableAttribute + extends ScalarAttribute { + const _NonInheritableAttribute() : super(null); + + @override + bool get isInheritable => false; }