Implementation
Style declarationsToStyle(Map<String, List<css.Expression>> declarations) {
Style style = Style();
declarations.forEach((property, value) {
if (value.isNotEmpty) {
switch (property) {
case 'background-color':
style.backgroundColor =
ExpressionMapping.expressionToColor(value.first) ??
style.backgroundColor;
break;
case 'border':
List<css.LiteralTerm?>? borderWidths =
value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.width], so make sure to remove those before passing it to [ExpressionMapping]
borderWidths.removeWhere((element) =>
element == null ||
(element.text != "thin" &&
element.text != "medium" &&
element.text != "thick" &&
element is! css.LengthTerm &&
element is! css.PercentageTerm &&
element is! css.EmTerm &&
element is! css.RemTerm &&
element is! css.NumberTerm));
List<css.Expression?>? borderColors = value
.where((element) =>
ExpressionMapping.expressionToColor(element) != null)
.toList();
List<css.LiteralTerm?>? potentialStyles =
value.whereType<css.LiteralTerm>().toList();
/// Currently doesn't matter, as Flutter only supports "solid" or "none", but may support more in the future.
List<String> possibleBorderValues = [
"dotted",
"dashed",
"solid",
"double",
"groove",
"ridge",
"inset",
"outset",
"none",
"hidden"
];
/// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.style], so make sure to remove those before passing it to [ExpressionMapping]
potentialStyles.removeWhere((element) =>
element == null || !possibleBorderValues.contains(element.text));
List<css.LiteralTerm?>? borderStyles = potentialStyles;
style.border = ExpressionMapping.expressionToBorder(
borderWidths, borderStyles, borderColors);
break;
case 'border-left':
List<css.LiteralTerm?>? borderWidths =
value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.width], so make sure to remove those before passing it to [ExpressionMapping]
borderWidths.removeWhere((element) =>
element == null ||
(element.text != "thin" &&
element.text != "medium" &&
element.text != "thick" &&
element is! css.LengthTerm &&
element is! css.PercentageTerm &&
element is! css.EmTerm &&
element is! css.RemTerm &&
element is! css.NumberTerm));
css.LiteralTerm? borderWidth =
borderWidths.firstWhereOrNull((element) => element != null);
css.Expression? borderColor = value.firstWhereOrNull((element) =>
ExpressionMapping.expressionToColor(element) != null);
List<css.LiteralTerm?>? potentialStyles =
value.whereType<css.LiteralTerm>().toList();
/// Currently doesn't matter, as Flutter only supports "solid" or "none", but may support more in the future.
List<String> possibleBorderValues = [
"dotted",
"dashed",
"solid",
"double",
"groove",
"ridge",
"inset",
"outset",
"none",
"hidden"
];
/// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.style], so make sure to remove those before passing it to [ExpressionMapping]
potentialStyles.removeWhere((element) =>
element == null || !possibleBorderValues.contains(element.text));
css.LiteralTerm? borderStyle = potentialStyles.firstOrNull;
Border newBorder = Border(
left: style.border?.left.copyWith(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor),
) ??
BorderSide(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor) ??
Colors.black,
),
right: style.border?.right ?? BorderSide.none,
top: style.border?.top ?? BorderSide.none,
bottom: style.border?.bottom ?? BorderSide.none,
);
style.border = newBorder;
break;
case 'border-right':
List<css.LiteralTerm?>? borderWidths =
value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.width], so make sure to remove those before passing it to [ExpressionMapping]
borderWidths.removeWhere((element) =>
element == null ||
(element.text != "thin" &&
element.text != "medium" &&
element.text != "thick" &&
element is! css.LengthTerm &&
element is! css.PercentageTerm &&
element is! css.EmTerm &&
element is! css.RemTerm &&
element is! css.NumberTerm));
css.LiteralTerm? borderWidth =
borderWidths.firstWhereOrNull((element) => element != null);
css.Expression? borderColor = value.firstWhereOrNull((element) =>
ExpressionMapping.expressionToColor(element) != null);
List<css.LiteralTerm?>? potentialStyles =
value.whereType<css.LiteralTerm>().toList();
/// Currently doesn't matter, as Flutter only supports "solid" or "none", but may support more in the future.
List<String> possibleBorderValues = [
"dotted",
"dashed",
"solid",
"double",
"groove",
"ridge",
"inset",
"outset",
"none",
"hidden"
];
/// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.style], so make sure to remove those before passing it to [ExpressionMapping]
potentialStyles.removeWhere((element) =>
element == null || !possibleBorderValues.contains(element.text));
css.LiteralTerm? borderStyle = potentialStyles.firstOrNull;
Border newBorder = Border(
left: style.border?.left ?? BorderSide.none,
right: style.border?.right.copyWith(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor),
) ??
BorderSide(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor) ??
Colors.black,
),
top: style.border?.top ?? BorderSide.none,
bottom: style.border?.bottom ?? BorderSide.none,
);
style.border = newBorder;
break;
case 'border-top':
List<css.LiteralTerm?>? borderWidths =
value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.width], so make sure to remove those before passing it to [ExpressionMapping]
borderWidths.removeWhere((element) =>
element == null ||
(element.text != "thin" &&
element.text != "medium" &&
element.text != "thick" &&
element is! css.LengthTerm &&
element is! css.PercentageTerm &&
element is! css.EmTerm &&
element is! css.RemTerm &&
element is! css.NumberTerm));
css.LiteralTerm? borderWidth =
borderWidths.firstWhereOrNull((element) => element != null);
css.Expression? borderColor = value.firstWhereOrNull((element) =>
ExpressionMapping.expressionToColor(element) != null);
List<css.LiteralTerm?>? potentialStyles =
value.whereType<css.LiteralTerm>().toList();
/// Currently doesn't matter, as Flutter only supports "solid" or "none", but may support more in the future.
List<String> possibleBorderValues = [
"dotted",
"dashed",
"solid",
"double",
"groove",
"ridge",
"inset",
"outset",
"none",
"hidden"
];
/// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.style], so make sure to remove those before passing it to [ExpressionMapping]
potentialStyles.removeWhere((element) =>
element == null || !possibleBorderValues.contains(element.text));
css.LiteralTerm? borderStyle = potentialStyles.firstOrNull;
Border newBorder = Border(
left: style.border?.left ?? BorderSide.none,
right: style.border?.right ?? BorderSide.none,
top: style.border?.top.copyWith(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor),
) ??
BorderSide(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor) ??
Colors.black,
),
bottom: style.border?.bottom ?? BorderSide.none,
);
style.border = newBorder;
break;
case 'border-bottom':
List<css.LiteralTerm?>? borderWidths =
value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.width], so make sure to remove those before passing it to [ExpressionMapping]
borderWidths.removeWhere((element) =>
element == null ||
(element.text != "thin" &&
element.text != "medium" &&
element.text != "thick" &&
element is! css.LengthTerm &&
element is! css.PercentageTerm &&
element is! css.EmTerm &&
element is! css.RemTerm &&
element is! css.NumberTerm));
css.LiteralTerm? borderWidth =
borderWidths.firstWhereOrNull((element) => element != null);
css.Expression? borderColor = value.firstWhereOrNull((element) =>
ExpressionMapping.expressionToColor(element) != null);
List<css.LiteralTerm?>? potentialStyles =
value.whereType<css.LiteralTerm>().toList();
/// Currently doesn't matter, as Flutter only supports "solid" or "none", but may support more in the future.
List<String> possibleBorderValues = [
"dotted",
"dashed",
"solid",
"double",
"groove",
"ridge",
"inset",
"outset",
"none",
"hidden"
];
/// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.style], so make sure to remove those before passing it to [ExpressionMapping]
potentialStyles.removeWhere((element) =>
element == null || !possibleBorderValues.contains(element.text));
css.LiteralTerm? borderStyle = potentialStyles.firstOrNull;
Border newBorder = Border(
left: style.border?.left ?? BorderSide.none,
right: style.border?.right ?? BorderSide.none,
top: style.border?.top ?? BorderSide.none,
bottom: style.border?.bottom.copyWith(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor),
) ??
BorderSide(
width: ExpressionMapping.expressionToBorderWidth(borderWidth),
style: ExpressionMapping.expressionToBorderStyle(borderStyle),
color: ExpressionMapping.expressionToColor(borderColor) ??
Colors.black,
),
);
style.border = newBorder;
break;
case 'color':
style.color =
ExpressionMapping.expressionToColor(value.first) ?? style.color;
break;
case 'direction':
style.direction =
ExpressionMapping.expressionToDirection(value.first);
break;
case 'display':
style.display = ExpressionMapping.expressionToDisplay(value.first);
break;
case 'line-height':
style.lineHeight =
ExpressionMapping.expressionToLineHeight(value.first);
break;
case 'font-family':
style.fontFamily =
ExpressionMapping.expressionToFontFamily(value.first) ??
style.fontFamily;
break;
case 'font-feature-settings':
style.fontFeatureSettings =
ExpressionMapping.expressionToFontFeatureSettings(value);
break;
case 'font-size':
style.fontSize =
ExpressionMapping.expressionToFontSize(value.first) ??
style.fontSize;
break;
case 'font-style':
style.fontStyle =
ExpressionMapping.expressionToFontStyle(value.first);
break;
case 'font-weight':
style.fontWeight =
ExpressionMapping.expressionToFontWeight(value.first);
break;
case 'list-style':
css.LiteralTerm? position = value.firstWhereOrNull((e) =>
e is css.LiteralTerm &&
(e.text == "outside" || e.text == "inside")) as css.LiteralTerm?;
css.UriTerm? image =
value.firstWhereOrNull((e) => e is css.UriTerm) as css.UriTerm?;
css.LiteralTerm? type = value.firstWhereOrNull((e) =>
e is css.LiteralTerm &&
e.text != "outside" &&
e.text != "inside") as css.LiteralTerm?;
if (position != null) {
switch (position.text) {
case 'outside':
style.listStylePosition = ListStylePosition.outside;
break;
case 'inside':
style.listStylePosition = ListStylePosition.inside;
break;
}
}
if (image != null) {
style.listStyleImage =
ExpressionMapping.expressionToListStyleImage(image) ??
style.listStyleImage;
} else if (type != null) {
style.listStyleType =
ExpressionMapping.expressionToListStyleType(type) ??
style.listStyleType;
}
break;
case 'list-style-image':
if (value.first is css.UriTerm) {
style.listStyleImage = ExpressionMapping.expressionToListStyleImage(
value.first as css.UriTerm) ??
style.listStyleImage;
}
break;
case 'list-style-position':
if (value.first is css.LiteralTerm) {
switch ((value.first as css.LiteralTerm).text) {
case 'outside':
style.listStylePosition = ListStylePosition.outside;
break;
case 'inside':
style.listStylePosition = ListStylePosition.inside;
break;
}
}
break;
case 'height':
style.height =
ExpressionMapping.expressionToHeight(value.first) ?? style.height;
break;
case 'list-style-type':
if (value.first is css.LiteralTerm) {
style.listStyleType = ExpressionMapping.expressionToListStyleType(
value.first as css.LiteralTerm) ??
style.listStyleType;
}
break;
case 'margin':
List<css.LiteralTerm>? marginLengths =
value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for margin length, so make sure to remove those before passing it to [ExpressionMapping]
marginLengths.removeWhere((element) =>
element is! css.LengthTerm &&
element is! css.EmTerm &&
element is! css.RemTerm &&
element is! css.NumberTerm &&
!(element.text == 'auto'));
Margins margin = ExpressionMapping.expressionToMargins(marginLengths);
style.margin = (style.margin ?? const Margins()).copyWith(
left: margin.left,
right: margin.right,
top: margin.top,
bottom: margin.bottom,
);
break;
case 'margin-left':
style.margin = (style.margin ?? const Margins()).copyWith(
left: ExpressionMapping.expressionToMargin(value.first));
break;
case 'margin-right':
style.margin = (style.margin ?? const Margins()).copyWith(
right: ExpressionMapping.expressionToMargin(value.first));
break;
case 'margin-top':
style.margin = (style.margin ?? const Margins())
.copyWith(top: ExpressionMapping.expressionToMargin(value.first));
break;
case 'margin-bottom':
style.margin = (style.margin ?? const Margins()).copyWith(
bottom: ExpressionMapping.expressionToMargin(value.first));
break;
case 'margin-inline':
List<css.LiteralTerm>? marginLengths =
value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for margin length, so make sure to remove those before passing it to [ExpressionMapping]
marginLengths.removeWhere((element) =>
element is! css.LengthTerm &&
element is! css.EmTerm &&
element is! css.RemTerm &&
element is! css.NumberTerm &&
!(element.text == 'auto'));
Margins margin =
ExpressionMapping.expressionToInlineMargins(marginLengths);
style.margin = (style.margin ?? const Margins()).copyWith(
inlineStart: margin.inlineStart,
inlineEnd: margin.inlineEnd,
);
break;
case 'margin-inline-end':
style.margin = (style.margin ?? const Margins()).copyWith(
inlineEnd: ExpressionMapping.expressionToMargin(value.first),
);
break;
case 'margin-inline-start':
style.margin = (style.margin ?? const Margins()).copyWith(
inlineStart: ExpressionMapping.expressionToMargin(value.first),
);
break;
case 'margin-block':
List<css.LiteralTerm>? marginLengths =
value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for margin length, so make sure to remove those before passing it to [ExpressionMapping]
marginLengths.removeWhere((element) =>
element is! css.LengthTerm &&
element is! css.EmTerm &&
element is! css.RemTerm &&
element is! css.NumberTerm &&
!(element.text == 'auto'));
Margins margin =
ExpressionMapping.expressionToBlockMargins(marginLengths);
style.margin = (style.margin ?? const Margins()).copyWith(
blockStart: margin.blockStart,
blockEnd: margin.blockEnd,
);
break;
case 'margin-block-end':
style.margin = (style.margin ?? const Margins()).copyWith(
blockEnd: ExpressionMapping.expressionToMargin(value.first),
);
break;
case 'margin-block-start':
style.margin = (style.margin ?? const Margins()).copyWith(
blockStart: ExpressionMapping.expressionToMargin(value.first),
);
break;
case 'padding':
List<css.LiteralTerm>? paddingLengths =
value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for padding length, so make sure to remove those before passing it to [ExpressionMapping]
paddingLengths.removeWhere((element) =>
element is! css.LengthTerm &&
element is! css.EmTerm &&
element is! css.RemTerm &&
element is! css.NumberTerm);
final padding =
ExpressionMapping.expressionToHtmlPaddings(paddingLengths);
style.padding = (style.padding ?? const HtmlPaddings()).copyWith(
left: padding.left,
right: padding.right,
top: padding.top,
bottom: padding.bottom,
);
break;
case 'padding-left':
style.padding = (style.padding ?? const HtmlPaddings()).copyWith(
left: ExpressionMapping.expressionToHtmlPadding(value.first));
break;
case 'padding-right':
style.padding = (style.padding ?? const HtmlPaddings()).copyWith(
right: ExpressionMapping.expressionToHtmlPadding(value.first));
break;
case 'padding-top':
style.padding = (style.padding ?? const HtmlPaddings()).copyWith(
top: ExpressionMapping.expressionToHtmlPadding(value.first));
break;
case 'padding-bottom':
style.padding = (style.padding ?? const HtmlPaddings()).copyWith(
bottom: ExpressionMapping.expressionToHtmlPadding(value.first));
break;
case 'padding-inline':
List<css.LiteralTerm>? paddingLengths =
value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for padding length, so make sure to remove those before passing it to [ExpressionMapping]
paddingLengths.removeWhere((element) =>
element is! css.LengthTerm &&
element is! css.EmTerm &&
element is! css.RemTerm &&
element is! css.NumberTerm);
HtmlPaddings padding =
ExpressionMapping.expressionToInlineHtmlPadding(paddingLengths);
style.padding = (style.padding ?? const HtmlPaddings()).copyWith(
inlineStart: padding.inlineStart,
inlineEnd: padding.inlineEnd,
);
break;
case 'padding-inline-end':
style.padding = (style.padding ?? const HtmlPaddings()).copyWith(
inlineEnd: ExpressionMapping.expressionToHtmlPadding(value.first),
);
break;
case 'padding-inline-start':
style.padding = (style.padding ?? const HtmlPaddings()).copyWith(
inlineStart: ExpressionMapping.expressionToHtmlPadding(value.first),
);
break;
case 'padding-block':
List<css.LiteralTerm>? paddingLengths =
value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for padding length, so make sure to remove those before passing it to [ExpressionMapping]
paddingLengths.removeWhere((element) =>
element is! css.LengthTerm &&
element is! css.EmTerm &&
element is! css.RemTerm &&
element is! css.NumberTerm);
HtmlPaddings padding =
ExpressionMapping.expressionToBlockHtmlPadding(paddingLengths);
style.padding = (style.padding ?? const HtmlPaddings()).copyWith(
blockStart: padding.blockStart,
blockEnd: padding.blockEnd,
);
break;
case 'padding-block-end':
style.padding = (style.padding ?? const HtmlPaddings()).copyWith(
blockEnd: ExpressionMapping.expressionToHtmlPadding(value.first),
);
break;
case 'padding-block-start':
style.padding = (style.padding ?? const HtmlPaddings()).copyWith(
blockStart: ExpressionMapping.expressionToHtmlPadding(value.first),
);
break;
case 'text-align':
style.textAlign =
ExpressionMapping.expressionToTextAlign(value.first);
break;
case 'text-decoration':
List<css.LiteralTerm?>? textDecorationList =
value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for [textDecorationList], so make sure to remove those before passing it to [ExpressionMapping]
textDecorationList.removeWhere((element) =>
element == null ||
(element.text != "none" &&
element.text != "overline" &&
element.text != "underline" &&
element.text != "line-through"));
List<css.Expression?>? nullableList = value;
css.Expression? textDecorationColor;
textDecorationColor = nullableList.firstWhereOrNull((element) =>
element is css.HexColorTerm || element is css.FunctionTerm);
List<css.LiteralTerm?>? potentialStyles =
value.whereType<css.LiteralTerm>().toList();
/// List<css.LiteralTerm> might include other values than the ones we want for [textDecorationStyle], so make sure to remove those before passing it to [ExpressionMapping]
potentialStyles.removeWhere((element) =>
element == null ||
(element.text != "solid" &&
element.text != "double" &&
element.text != "dashed" &&
element.text != "dotted" &&
element.text != "wavy"));
css.LiteralTerm? textDecorationStyle =
potentialStyles.isNotEmpty ? potentialStyles.last : null;
style.textDecoration =
ExpressionMapping.expressionToTextDecorationLine(
textDecorationList);
if (textDecorationColor != null) {
style.textDecorationColor =
ExpressionMapping.expressionToColor(textDecorationColor) ??
style.textDecorationColor;
}
if (textDecorationStyle != null) {
style.textDecorationStyle =
ExpressionMapping.expressionToTextDecorationStyle(
textDecorationStyle);
}
break;
case 'text-decoration-color':
style.textDecorationColor =
ExpressionMapping.expressionToColor(value.first) ??
style.textDecorationColor;
break;
case 'text-decoration-line':
List<css.LiteralTerm?>? textDecorationList =
value.whereType<css.LiteralTerm>().toList();
style.textDecoration =
ExpressionMapping.expressionToTextDecorationLine(
textDecorationList);
break;
case 'text-decoration-style':
style.textDecorationStyle =
ExpressionMapping.expressionToTextDecorationStyle(
value.first as css.LiteralTerm);
break;
case 'text-shadow':
style.textShadow = ExpressionMapping.expressionToTextShadow(value);
break;
case 'text-transform':
final val = (value.first as css.LiteralTerm).text;
if (val == 'uppercase') {
style.textTransform = TextTransform.uppercase;
} else if (val == 'lowercase') {
style.textTransform = TextTransform.lowercase;
} else if (val == 'capitalize') {
style.textTransform = TextTransform.capitalize;
} else {
style.textTransform = TextTransform.none;
}
break;
case 'vertical-align':
style.verticalAlign =
ExpressionMapping.expressionToVerticalAlign(value.first);
break;
case 'width':
style.width =
ExpressionMapping.expressionToWidth(value.first) ?? style.width;
break;
}
}
});
return style;
}