Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/styled icon supporting decorators #159

Merged
merged 11 commits into from
Jan 22, 2024
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
Loading