diff --git a/example/lib/main.dart b/example/lib/main.dart
index e0489a248c..363f4279e3 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -55,10 +55,20 @@ const htmlData = r"""
The should be BLACK with 10% alpha style='color: rgba(0, 0, 0, 0.10);
The should be GREEN style='color: rgb(0, 97, 0);
The should be GREEN style='color: rgb(0, 97, 0);
- blasdafjklasdlkjfkl
- blasdafjklasdlkjfkl
- blasdafjklasdlkjfkl
- blasdafjklasdlkjfkl
+ Text Alignment
+ Center Aligned Text
+ Right Aligned Text
+ Justified Text
+ Center Aligned Text
+ Auto Margins
+ Default Div
+ margin: auto
+ margin: 15px auto
+ margin-left: auto
+ With an image - non-block (should not center):
+
+ block image (should center):
+
Table support (with custom styling!):
Famous quote...
diff --git a/lib/html_parser.dart b/lib/html_parser.dart
index 2f66cc9a36..1b401ae435 100644
--- a/lib/html_parser.dart
+++ b/lib/html_parser.dart
@@ -652,7 +652,7 @@ class HtmlParser extends StatelessWidget {
if (tree.children.isEmpty) {
// Handle case (4) from above.
if ((tree.style.height ?? 0) == 0) {
- tree.style.margin = EdgeInsets.zero;
+ tree.style.margin = tree.style.margin?.collapse() ?? Margins.zero;
}
return tree;
}
@@ -668,47 +668,47 @@ class HtmlParser extends StatelessWidget {
// Handle case (1) from above.
// Top margins cannot collapse if the element has padding
if ((tree.style.padding?.top ?? 0) == 0) {
- final parentTop = tree.style.margin?.top ?? 0;
- final firstChildTop = tree.children.first.style.margin?.top ?? 0;
+ final parentTop = tree.style.margin?.top?.value ?? 0;
+ final firstChildTop = tree.children.first.style.margin?.top?.value ?? 0;
final newOuterMarginTop = max(parentTop, firstChildTop);
// Set the parent's margin
if (tree.style.margin == null) {
- tree.style.margin = EdgeInsets.only(top: newOuterMarginTop);
+ tree.style.margin = Margins.only(top: newOuterMarginTop);
} else {
- tree.style.margin = tree.style.margin!.copyWith(top: newOuterMarginTop);
+ tree.style.margin = tree.style.margin!.copyWithEdge(top: newOuterMarginTop);
}
// And remove the child's margin
if (tree.children.first.style.margin == null) {
- tree.children.first.style.margin = EdgeInsets.zero;
+ tree.children.first.style.margin = Margins.zero;
} else {
tree.children.first.style.margin =
- tree.children.first.style.margin!.copyWith(top: 0);
+ tree.children.first.style.margin!.copyWithEdge(top: 0);
}
}
// Handle case (3) from above.
// Bottom margins cannot collapse if the element has padding
if ((tree.style.padding?.bottom ?? 0) == 0) {
- final parentBottom = tree.style.margin?.bottom ?? 0;
- final lastChildBottom = tree.children.last.style.margin?.bottom ?? 0;
+ final parentBottom = tree.style.margin?.bottom?.value ?? 0;
+ final lastChildBottom = tree.children.last.style.margin?.bottom?.value ?? 0;
final newOuterMarginBottom = max(parentBottom, lastChildBottom);
// Set the parent's margin
if (tree.style.margin == null) {
- tree.style.margin = EdgeInsets.only(bottom: newOuterMarginBottom);
+ tree.style.margin = Margins.only(bottom: newOuterMarginBottom);
} else {
tree.style.margin =
- tree.style.margin!.copyWith(bottom: newOuterMarginBottom);
+ tree.style.margin!.copyWithEdge(bottom: newOuterMarginBottom);
}
// And remove the child's margin
if (tree.children.last.style.margin == null) {
- tree.children.last.style.margin = EdgeInsets.zero;
+ tree.children.last.style.margin = Margins.zero;
} else {
tree.children.last.style.margin =
- tree.children.last.style.margin!.copyWith(bottom: 0);
+ tree.children.last.style.margin!.copyWithEdge(bottom: 0);
}
}
@@ -716,24 +716,24 @@ class HtmlParser extends StatelessWidget {
if (tree.children.length > 1) {
for (int i = 1; i < tree.children.length; i++) {
final previousSiblingBottom =
- tree.children[i - 1].style.margin?.bottom ?? 0;
- final thisTop = tree.children[i].style.margin?.top ?? 0;
+ tree.children[i - 1].style.margin?.bottom?.value ?? 0;
+ final thisTop = tree.children[i].style.margin?.top?.value ?? 0;
final newInternalMargin = max(previousSiblingBottom, thisTop) / 2;
if (tree.children[i - 1].style.margin == null) {
tree.children[i - 1].style.margin =
- EdgeInsets.only(bottom: newInternalMargin);
+ Margins.only(bottom: newInternalMargin);
} else {
tree.children[i - 1].style.margin = tree.children[i - 1].style.margin!
- .copyWith(bottom: newInternalMargin);
+ .copyWithEdge(bottom: newInternalMargin);
}
if (tree.children[i].style.margin == null) {
tree.children[i].style.margin =
- EdgeInsets.only(top: newInternalMargin);
+ Margins.only(top: newInternalMargin);
} else {
tree.children[i].style.margin =
- tree.children[i].style.margin!.copyWith(top: newInternalMargin);
+ tree.children[i].style.margin!.copyWithEdge(top: newInternalMargin);
}
}
}
@@ -847,7 +847,14 @@ class ContainerSpan extends StatelessWidget {
@override
Widget build(BuildContext _) {
- return Container(
+
+ // Elements that are inline should ignore margin: auto for alignment.
+ var alignment = shrinkWrap ? null : style.alignment;
+ if(style.display == Display.BLOCK) {
+ alignment = style.margin?.alignment ?? alignment;
+ }
+
+ Widget container = Container(
decoration: BoxDecoration(
border: style.border,
color: style.backgroundColor,
@@ -855,8 +862,8 @@ class ContainerSpan extends StatelessWidget {
height: style.height,
width: style.width,
padding: style.padding?.nonNegative,
- margin: style.margin?.nonNegative,
- alignment: shrinkWrap ? null : style.alignment,
+ margin: style.margin?.asInsets.nonNegative,
+ alignment: alignment,
child: child ??
StyledText(
textSpan: TextSpan(
@@ -867,6 +874,8 @@ class ContainerSpan extends StatelessWidget {
renderContext: newContext,
),
);
+
+ return container;
}
}
diff --git a/lib/src/css_parser.dart b/lib/src/css_parser.dart
index acc4724cc2..5ac92caf27 100644
--- a/lib/src/css_parser.dart
+++ b/lib/src/css_parser.dart
@@ -244,30 +244,31 @@ Style declarationsToStyle(Map> declarations) {
&& !(element is css.EmTerm)
&& !(element is css.RemTerm)
&& !(element is css.NumberTerm)
+ && !(element.text == 'auto')
);
- List margin = ExpressionMapping.expressionToPadding(marginLengths);
- style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
- left: margin[0],
- right: margin[1],
- top: margin[2],
- bottom: margin[3],
+ Margins margin = ExpressionMapping.expressionToMargins(marginLengths);
+ style.margin = (style.margin ?? Margins.all(0)).copyWith(
+ left: margin.left,
+ right: margin.right,
+ top: margin.top,
+ bottom: margin.bottom,
);
break;
case 'margin-left':
- style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
- left: ExpressionMapping.expressionToPaddingLength(value.first));
+ style.margin = (style.margin ?? Margins.zero).copyWith(
+ left: ExpressionMapping.expressionToMargin(value.first));
break;
case 'margin-right':
- style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
- right: ExpressionMapping.expressionToPaddingLength(value.first));
+ style.margin = (style.margin ?? Margins.zero).copyWith(
+ right: ExpressionMapping.expressionToMargin(value.first));
break;
case 'margin-top':
- style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
- top: ExpressionMapping.expressionToPaddingLength(value.first));
+ style.margin = (style.margin ?? Margins.zero).copyWith(
+ top: ExpressionMapping.expressionToMargin(value.first));
break;
case 'margin-bottom':
- style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
- bottom: ExpressionMapping.expressionToPaddingLength(value.first));
+ style.margin = (style.margin ?? Margins.zero).copyWith(
+ bottom: ExpressionMapping.expressionToMargin(value.first));
break;
case 'padding':
List? paddingLengths = value.whereType().toList();
@@ -748,6 +749,45 @@ class ExpressionMapping {
return null;
}
+ static Margin? expressionToMargin(css.Expression value) {
+ if ((value is css.LiteralTerm) && value.text == 'auto') {
+ return AutoMargin();
+ } else {
+ return InsetMargin(expressionToPaddingLength(value) ?? 0);
+ }
+ }
+
+ static Margins expressionToMargins(List? lengths) {
+ Margin? left;
+ Margin? right;
+ Margin? top;
+ Margin? bottom;
+ if (lengths != null && lengths.isNotEmpty) {
+ top = expressionToMargin(lengths.first);
+ if (lengths.length == 4) {
+ right = expressionToMargin(lengths[1]);
+ bottom = expressionToMargin(lengths[2]);
+ left = expressionToMargin(lengths.last);
+ }
+ if (lengths.length == 3) {
+ left = expressionToMargin(lengths[1]);
+ right = expressionToMargin(lengths[1]);
+ bottom = expressionToMargin(lengths.last);
+ }
+ if (lengths.length == 2) {
+ bottom = expressionToMargin(lengths.first);
+ left = expressionToMargin(lengths.last);
+ right = expressionToMargin(lengths.last);
+ }
+ if (lengths.length == 1) {
+ bottom = expressionToMargin(lengths.first);
+ left = expressionToMargin(lengths.first);
+ right = expressionToMargin(lengths.first);
+ }
+ }
+ return Margins(left: left, right: right, top: top, bottom: bottom);
+ }
+
static List expressionToPadding(List? lengths) {
double? left;
double? right;
diff --git a/lib/src/styled_element.dart b/lib/src/styled_element.dart
index 9561d8f228..9531f5bcf9 100644
--- a/lib/src/styled_element.dart
+++ b/lib/src/styled_element.dart
@@ -102,19 +102,19 @@ StyledElement parseStyledElement(
//TODO(Sub6Resources) this is a workaround for collapsing margins. Remove.
if (element.parent!.localName == "blockquote") {
styledElement.style = Style(
- margin: const EdgeInsets.only(left: 40.0, right: 40.0, bottom: 14.0),
+ margin: Margins.only(left: 40.0, right: 40.0, bottom: 14.0),
display: Display.BLOCK,
);
} else {
styledElement.style = Style(
- margin: const EdgeInsets.symmetric(horizontal: 40.0, vertical: 14.0),
+ margin: Margins.symmetric(horizontal: 40.0, vertical: 14.0),
display: Display.BLOCK,
);
}
break;
case "body":
styledElement.style = Style(
- margin: EdgeInsets.all(8.0),
+ margin: Margins.all(8.0),
display: Display.BLOCK,
);
break;
@@ -134,7 +134,7 @@ StyledElement parseStyledElement(
break;
case "dd":
styledElement.style = Style(
- margin: EdgeInsets.only(left: 40.0),
+ margin: Margins.only(left: 40.0),
display: Display.BLOCK,
);
break;
@@ -148,13 +148,13 @@ StyledElement parseStyledElement(
continue italics;
case "div":
styledElement.style = Style(
- margin: EdgeInsets.all(0),
+ margin: Margins.all(0),
display: Display.BLOCK,
);
break;
case "dl":
styledElement.style = Style(
- margin: EdgeInsets.symmetric(vertical: 14.0),
+ margin: Margins.symmetric(vertical: 14.0),
display: Display.BLOCK,
);
break;
@@ -172,7 +172,7 @@ StyledElement parseStyledElement(
break;
case "figure":
styledElement.style = Style(
- margin: EdgeInsets.symmetric(vertical: 14.0, horizontal: 40.0),
+ margin: Margins.symmetric(vertical: 14.0, horizontal: 40.0),
display: Display.BLOCK,
);
break;
@@ -196,7 +196,7 @@ StyledElement parseStyledElement(
styledElement.style = Style(
fontSize: FontSize.xxLarge,
fontWeight: FontWeight.bold,
- margin: EdgeInsets.symmetric(vertical: 18.67),
+ margin: Margins.symmetric(vertical: 18.67),
display: Display.BLOCK,
);
break;
@@ -204,7 +204,7 @@ StyledElement parseStyledElement(
styledElement.style = Style(
fontSize: FontSize.xLarge,
fontWeight: FontWeight.bold,
- margin: EdgeInsets.symmetric(vertical: 17.5),
+ margin: Margins.symmetric(vertical: 17.5),
display: Display.BLOCK,
);
break;
@@ -212,7 +212,7 @@ StyledElement parseStyledElement(
styledElement.style = Style(
fontSize: FontSize(16.38),
fontWeight: FontWeight.bold,
- margin: EdgeInsets.symmetric(vertical: 16.5),
+ margin: Margins.symmetric(vertical: 16.5),
display: Display.BLOCK,
);
break;
@@ -220,7 +220,7 @@ StyledElement parseStyledElement(
styledElement.style = Style(
fontSize: FontSize.medium,
fontWeight: FontWeight.bold,
- margin: EdgeInsets.symmetric(vertical: 18.5),
+ margin: Margins.symmetric(vertical: 18.5),
display: Display.BLOCK,
);
break;
@@ -228,7 +228,7 @@ StyledElement parseStyledElement(
styledElement.style = Style(
fontSize: FontSize(11.62),
fontWeight: FontWeight.bold,
- margin: EdgeInsets.symmetric(vertical: 19.25),
+ margin: Margins.symmetric(vertical: 19.25),
display: Display.BLOCK,
);
break;
@@ -236,7 +236,7 @@ StyledElement parseStyledElement(
styledElement.style = Style(
fontSize: FontSize(9.38),
fontWeight: FontWeight.bold,
- margin: EdgeInsets.symmetric(vertical: 22),
+ margin: Margins.symmetric(vertical: 22),
display: Display.BLOCK,
);
break;
@@ -247,7 +247,7 @@ StyledElement parseStyledElement(
break;
case "hr":
styledElement.style = Style(
- margin: EdgeInsets.symmetric(vertical: 7.0),
+ margin: Margins.symmetric(vertical: 7.0),
width: double.infinity,
height: 1,
backgroundColor: Colors.black,
@@ -318,14 +318,14 @@ StyledElement parseStyledElement(
break;
case "p":
styledElement.style = Style(
- margin: EdgeInsets.symmetric(vertical: 14.0),
+ margin: Margins.symmetric(vertical: 14.0),
display: Display.BLOCK,
);
break;
case "pre":
styledElement.style = Style(
fontFamily: 'monospace',
- margin: EdgeInsets.symmetric(vertical: 14.0),
+ margin: Margins.symmetric(vertical: 14.0),
whiteSpace: WhiteSpace.PRE,
display: Display.BLOCK,
);
diff --git a/lib/style.dart b/lib/style.dart
index 95bde15088..12f777ae37 100644
--- a/lib/style.dart
+++ b/lib/style.dart
@@ -103,7 +103,7 @@ class Style {
///
/// Inherited: no,
/// Default: EdgeInsets.zero
- EdgeInsets? margin;
+ Margins? margin;
/// CSS attribute "`text-align`"
///
@@ -391,7 +391,7 @@ class Style {
ListStyleType? listStyleType,
ListStylePosition? listStylePosition,
EdgeInsets? padding,
- EdgeInsets? margin,
+ Margins? margin,
TextAlign? textAlign,
TextDecoration? textDecoration,
Color? textDecorationColor,
@@ -481,6 +481,97 @@ enum Display {
NONE,
}
+abstract class Margin {
+ const Margin();
+
+ double get value => this is InsetMargin ? this.value : 0;
+ }
+
+class AutoMargin extends Margin {
+ const AutoMargin();
+}
+
+class InsetMargin extends Margin {
+ final double value;
+ const InsetMargin(this.value);
+}
+
+class Margins {
+ final Margin? left;
+ final Margin? right;
+ final Margin? top;
+ final Margin? bottom;
+
+ const Margins({ this.left, this.right, this.top, this.bottom });
+
+ /// Auto margins already have a "value" of zero so can be considered collapsed.
+ Margins collapse() => Margins(
+ left: left is AutoMargin ? left : InsetMargin(0),
+ right: right is AutoMargin ? right : InsetMargin(0),
+ top: top is AutoMargin ? top : InsetMargin(0),
+ bottom: bottom is AutoMargin ? bottom : InsetMargin(0),
+ );
+
+ Margins copyWith({ Margin? left, Margin? right, Margin? top, Margin? bottom }) => Margins(
+ left: left ?? this.left,
+ right: right ?? this.right,
+ top: top ?? this.top,
+ bottom: bottom ?? this.bottom,
+ );
+
+ Margins copyWithEdge({ double? left, double? right, double? top, double? bottom }) => Margins(
+ left: left != null ? InsetMargin(left) : this.left,
+ right: right != null ? InsetMargin(right) : this.right,
+ top: top != null ? InsetMargin(top) : this.top,
+ bottom: bottom != null ? InsetMargin(bottom) : this.bottom,
+ );
+
+ bool get isAutoHorizontal => (left is AutoMargin) || (right is AutoMargin);
+
+ Alignment? get alignment {
+ if((left is AutoMargin) && (right is AutoMargin)) {
+ return Alignment.center;
+ } else if(left is AutoMargin) {
+ return Alignment.topRight;
+ }
+ }
+
+ /// Analogous to [EdgeInsets.zero]
+ static Margins get zero => Margins.all(0);
+
+ /// Analogous to [EdgeInsets.all]
+ static Margins all(double value) => Margins(
+ left: InsetMargin(value),
+ right: InsetMargin(value),
+ top: InsetMargin(value),
+ bottom: InsetMargin(value),
+ );
+
+ /// Analogous to [EdgeInsets.only]
+ static Margins only({ double? left, double? right, double? top, double? bottom }) => Margins(
+ left: InsetMargin(left ?? 0),
+ right: InsetMargin(right ?? 0),
+ top: InsetMargin(top ?? 0),
+ bottom: InsetMargin(bottom ?? 0),
+ );
+
+
+ /// Analogous to [EdgeInsets.symmetric]
+ static Margins symmetric({double? horizontal, double? vertical}) => Margins(
+ left: InsetMargin(horizontal ?? 0),
+ right: InsetMargin(horizontal ?? 0),
+ top: InsetMargin(vertical ?? 0),
+ bottom: InsetMargin(vertical ?? 0),
+ );
+
+ EdgeInsets get asInsets => EdgeInsets.zero.copyWith(
+ left: left?.value ?? 0,
+ right: right?.value ?? 0,
+ top: top?.value ?? 0,
+ bottom: bottom?.value ?? 0,
+ );
+}
+
class FontSize {
final double? size;
final String units;
diff --git a/packages/flutter_html_table/lib/flutter_html_table.dart b/packages/flutter_html_table/lib/flutter_html_table.dart
index c65387e9c2..451e0dd505 100644
--- a/packages/flutter_html_table/lib/flutter_html_table.dart
+++ b/packages/flutter_html_table/lib/flutter_html_table.dart
@@ -11,7 +11,7 @@ CustomRender tableRender() =>
CustomRender.widget(widget: (context, buildChildren) {
return Container(
key: context.key,
- margin: context.style.margin?.nonNegative,
+ margin: context.style.margin?.asInsets.nonNegative,
padding: context.style.padding?.nonNegative,
alignment: context.style.alignment,
decoration: BoxDecoration(