Skip to content

Commit

Permalink
Feature/styled icon supporting decorators (#159)
Browse files Browse the repository at this point in the history
* Create test cases to heritage

* Add support for decorator attributes in StyledBox

* create a test case to decorators

* Add rule that decorator is only applied to the first widget

* Update widget_decorator_widget_test.dart

* Update the decorators doc.

* add logic to remove decorators from inheritedMix in StyledWidget

Create a where constructor in MixData

* Remove unused functions

* Add isInheritable property

remove RenderWidgetDecorators when there isnt decorators attributes in style

* review the code

* Solve comments
  • Loading branch information
tilucasoli authored Jan 22, 2024
1 parent 67ab5ec commit 4bbd2be
Show file tree
Hide file tree
Showing 15 changed files with 537 additions and 25 deletions.
3 changes: 3 additions & 0 deletions lib/src/core/attribute.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions lib/src/core/decorator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ abstract class Decorator<Self extends Decorator<Self>> extends StyleAttribute {
@override
Object get type => Self;

@override
bool get isInheritable => false;

Widget build(MixData mix, Widget child);
}

Expand Down
2 changes: 1 addition & 1 deletion lib/src/core/styled_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +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) {
final inheritedMix = inherit ? MixProvider.maybeOf(context) : null;
final inheritedMix = inherit ? MixData.inherited(context) : null;

final mixData = MixData.create(context, style);

Expand Down
25 changes: 24 additions & 1 deletion lib/src/factory/mix_provider_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -33,7 +34,25 @@ class MixData with Comparable {
final resolver = MixTokenResolver(context);

return MixData._(
resolver: resolver, attributes: AttributeMap(attributeList));
resolver: resolver,
attributes: AttributeMap(attributeList),
);
}

static MixData? inherited(BuildContext context) {
final inheritedMix = MixProvider.maybeOf(context);

if (inheritedMix == null) return null;

// Remove non-inheritable attributes
final inheritableAttributes = inheritedMix.attributes.values.where(
(attr) => attr.isInheritable,
);

return MixData._(
resolver: MixTokenResolver(context),
attributes: AttributeMap(inheritableAttributes),
);
}

/// Getter for [MixTokenResolver].
Expand All @@ -59,6 +78,10 @@ class MixData with Comparable {
return _attributes.whereType<A>();
}

bool contains<T>() {
return _attributes.values.any((attr) => attr is T);
}

Value resolvableOf<Value, A extends SpecAttribute<A, Value>>(A attribute) {
final attributes = _attributes.whereType<A>();
if (attributes.isEmpty) return attribute.resolve(this);
Expand Down
30 changes: 16 additions & 14 deletions lib/src/specs/container/box_widget.dart
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -115,21 +115,23 @@ class MixedBox extends StatelessWidget {
final spec = BoxSpec.of(mix);

// Apply styles and decorators to the Container, which wraps the child widget.
return RenderWidgetDecorators(
final container = Container(
alignment: spec.alignment,
padding: spec.padding,
decoration: spec.decoration,
width: spec.width,
height: spec.height,
constraints: spec.constraints,
margin: spec.margin,
transform: spec.transform,
clipBehavior: spec.clipBehavior ?? Clip.none,
child: child,
);

return shouldApplyDecorators(
mix: mix,
orderOfDecorators: decoratorOrder,
child: Container(
alignment: spec.alignment,
padding: spec.padding,
decoration: spec.decoration,
width: spec.width,
height: spec.height,
constraints: spec.constraints,
margin: spec.margin,
transform: spec.transform,
clipBehavior: spec.clipBehavior ?? Clip.none,
child: child,
),
child: container,
);
}
}
Expand Down
13 changes: 11 additions & 2 deletions lib/src/specs/flex/flex_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import '../../deprecations.dart';
import '../../factory/mix_provider.dart';
import '../../factory/mix_provider_data.dart';
import '../../factory/style_mix.dart';
import '../../utils/helper_util.dart';
import '../container/box_widget.dart';
import 'flex_spec.dart';

Expand Down Expand Up @@ -48,14 +49,16 @@ class StyledFlex extends StyledWidget {

class MixedFlex extends StatelessWidget {
const MixedFlex({
this.mix,
super.key,
this.mix,
this.decoratorOrder = const [],
required this.children,
required this.direction,
});

final List<Widget> children;
final Axis direction;
final List<Type> decoratorOrder;
final MixData? mix;

@override
Expand All @@ -64,7 +67,7 @@ class MixedFlex extends StatelessWidget {
final spec = FlexSpec.of(mix);
final gap = spec.gap;

return Flex(
var flexWidget = Flex(
direction: direction,
mainAxisAlignment:
spec.mainAxisAlignment ?? _defaultFlex.mainAxisAlignment,
Expand All @@ -75,6 +78,12 @@ class MixedFlex extends StatelessWidget {
spec.verticalDirection ?? _defaultFlex.verticalDirection,
children: buildChildren(gap),
);

return shouldApplyDecorators(
mix: mix,
orderOfDecorators: decoratorOrder,
child: flexWidget,
);
}

@visibleForTesting
Expand Down
11 changes: 10 additions & 1 deletion lib/src/specs/icon/icon_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import '../../core/styled_widget.dart';
import '../../factory/mix_provider.dart';
import '../../factory/mix_provider_data.dart';
import '../../utils/helper_util.dart';
import 'icon_attribute.dart';
import 'icon_spec.dart';

Expand Down Expand Up @@ -39,24 +40,32 @@ class MixedIcon extends StatelessWidget {
this.semanticLabel,
super.key,
this.textDirection,
this.decoratorOrder = const [],
});

final IconData? icon;
final MixData? mix;
final String? semanticLabel;
final TextDirection? textDirection;
final List<Type> decoratorOrder;

@override
Widget build(BuildContext context) {
final mix = this.mix ?? MixProvider.of(context);
final spec = IconSpec.of(mix);

return IconSpecWidget(
final iconWidget = IconSpecWidget(
spec: spec,
semanticLabel: semanticLabel,
textDirection: textDirection,
icon: icon,
);

return shouldApplyDecorators(
mix: mix,
orderOfDecorators: decoratorOrder,
child: iconWidget,
);
}
}

Expand Down
11 changes: 10 additions & 1 deletion lib/src/specs/text/text_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import '../../core/directive.dart';
import '../../core/styled_widget.dart';
import '../../factory/mix_provider.dart';
import '../../factory/mix_provider_data.dart';
import '../../utils/helper_util.dart';
import 'text_spec.dart';

/// [StyledText] - A styled widget for displaying text with a mix of styles.
Expand Down Expand Up @@ -80,13 +81,15 @@ class MixedText extends StatelessWidget {
this.mix,
this.semanticsLabel,
this.locale,
this.decoratorOrder = const [],
super.key,
});

final String text;
final String? semanticsLabel;
final Locale? locale;
final MixData? mix;
final List<Type> decoratorOrder;

@override
Widget build(BuildContext context) {
Expand All @@ -98,7 +101,7 @@ class MixedText extends StatelessWidget {
final modifyText = mix.attributeOf<TextDataDirective>();

// The Text widget is used here, applying the resolved styles and properties from TextSpec.
return Text(
final textWidget = Text(
modifyText?.apply(text) ?? text,
style: spec.style,
strutStyle: spec.strutStyle,
Expand All @@ -113,5 +116,11 @@ class MixedText extends StatelessWidget {
textWidthBasis: spec.textWidthBasis,
textHeightBehavior: spec.textHeightBehavior,
);

return shouldApplyDecorators(
mix: mix,
orderOfDecorators: decoratorOrder,
child: textWidget,
);
}
}
20 changes: 20 additions & 0 deletions lib/src/utils/helper_util.dart
Original file line number Diff line number Diff line change
@@ -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<ParamT, ReturnT> = ReturnT Function(
List<ParamT> params,
);
Expand Down Expand Up @@ -35,3 +41,17 @@ class SpreadFunctionParams<ParamT, ReturnT> {
].whereType<ParamT>().toList());
}
}

Widget shouldApplyDecorators({
required MixData mix,
required Widget child,
List<Type> orderOfDecorators = const [],
}) {
return mix.contains<WidgetDecorator>()
? RenderWidgetDecorators(
mix: mix,
orderOfDecorators: orderOfDecorators,
child: child,
)
: child;
}
14 changes: 14 additions & 0 deletions test/helpers/testing_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,17 @@ class CustomWidgetDecorator extends WidgetDecorator<CustomWidgetDecorator> {
@override
get props => [];
}

class WidgetWithTestableBuild extends StyledWidget {
const WidgetWithTestableBuild(this.onBuild, {super.key});

final void Function(BuildContext context) onBuild;

@override
Widget build(BuildContext context) {
return withMix(context, (_) {
onBuild(context);
return const SizedBox();
});
}
}
Loading

0 comments on commit 4bbd2be

Please sign in to comment.