Skip to content

Commit 65dbe79

Browse files
Merge pull request #879 from NextFaze/feature/auto-margins
Add support for auto horizontal margins
2 parents ee9f478 + bb3be56 commit 65dbe79

File tree

6 files changed

+209
-59
lines changed

6 files changed

+209
-59
lines changed

example/lib/main.dart

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,20 @@ const htmlData = r"""
5555
<p>The should be <span style='color: rgba(0, 0, 0, 0.10);'>BLACK with 10% alpha style='color: rgba(0, 0, 0, 0.10);</span></p>
5656
<p>The should be <span style='color: rgb(0, 97, 0);'>GREEN style='color: rgb(0, 97, 0);</span></p>
5757
<p>The should be <span style='background-color: red; color: rgb(0, 97, 0);'>GREEN style='color: rgb(0, 97, 0);</span></p>
58-
<p style="text-align: center;"><span style="color: rgba(0, 0, 0, 0.95);">blasdafjklasdlkjfkl</span></p>
59-
<p style="text-align: right;"><span style="color: rgba(0, 0, 0, 0.95);">blasdafjklasdlkjfkl</span></p>
60-
<p style="text-align: justify;"><span style="color: rgba(0, 0, 0, 0.95);">blasdafjklasdlkjfkl</span></p>
61-
<p style="text-align: center;"><span style="color: rgba(0, 0, 0, 0.95);">blasdafjklasdlkjfkl</span></p>
58+
<h3>Text Alignment</h3>
59+
<p style="text-align: center;"><span style="color: rgba(0, 0, 0, 0.95);">Center Aligned Text</span></p>
60+
<p style="text-align: right;"><span style="color: rgba(0, 0, 0, 0.95);">Right Aligned Text</span></p>
61+
<p style="text-align: justify;"><span style="color: rgba(0, 0, 0, 0.95);">Justified Text</span></p>
62+
<p style="text-align: center;"><span style="color: rgba(0, 0, 0, 0.95);">Center Aligned Text</span></p>
63+
<h3>Auto Margins</h3>
64+
<div style="width: 150px; height: 20px; background-color: #ff9999;">Default Div</div>
65+
<div style="width: 150px; height: 20px; background-color: #99ff99; margin: auto;">margin: auto</div>
66+
<div style="width: 150px; height: 20px; background-color: #ff99ff; margin: 15px auto;">margin: 15px auto</div>
67+
<div style="width: 150px; height: 20px; background-color: #9999ff; margin-left: auto;">margin-left: auto</div>
68+
<p>With an image - non-block (should not center):</p>
69+
<img style="margin: auto;" src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png">
70+
<p>block image (should center):</p>
71+
<img style="display: block; margin: auto;" src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png">
6272
<h3>Table support (with custom styling!):</h3>
6373
<p>
6474
<q>Famous quote...</q>

lib/html_parser.dart

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ class HtmlParser extends StatelessWidget {
652652
if (tree.children.isEmpty) {
653653
// Handle case (4) from above.
654654
if ((tree.style.height ?? 0) == 0) {
655-
tree.style.margin = EdgeInsets.zero;
655+
tree.style.margin = tree.style.margin?.collapse() ?? Margins.zero;
656656
}
657657
return tree;
658658
}
@@ -668,72 +668,72 @@ class HtmlParser extends StatelessWidget {
668668
// Handle case (1) from above.
669669
// Top margins cannot collapse if the element has padding
670670
if ((tree.style.padding?.top ?? 0) == 0) {
671-
final parentTop = tree.style.margin?.top ?? 0;
672-
final firstChildTop = tree.children.first.style.margin?.top ?? 0;
671+
final parentTop = tree.style.margin?.top?.value ?? 0;
672+
final firstChildTop = tree.children.first.style.margin?.top?.value ?? 0;
673673
final newOuterMarginTop = max(parentTop, firstChildTop);
674674

675675
// Set the parent's margin
676676
if (tree.style.margin == null) {
677-
tree.style.margin = EdgeInsets.only(top: newOuterMarginTop);
677+
tree.style.margin = Margins.only(top: newOuterMarginTop);
678678
} else {
679-
tree.style.margin = tree.style.margin!.copyWith(top: newOuterMarginTop);
679+
tree.style.margin = tree.style.margin!.copyWithEdge(top: newOuterMarginTop);
680680
}
681681

682682
// And remove the child's margin
683683
if (tree.children.first.style.margin == null) {
684-
tree.children.first.style.margin = EdgeInsets.zero;
684+
tree.children.first.style.margin = Margins.zero;
685685
} else {
686686
tree.children.first.style.margin =
687-
tree.children.first.style.margin!.copyWith(top: 0);
687+
tree.children.first.style.margin!.copyWithEdge(top: 0);
688688
}
689689
}
690690

691691
// Handle case (3) from above.
692692
// Bottom margins cannot collapse if the element has padding
693693
if ((tree.style.padding?.bottom ?? 0) == 0) {
694-
final parentBottom = tree.style.margin?.bottom ?? 0;
695-
final lastChildBottom = tree.children.last.style.margin?.bottom ?? 0;
694+
final parentBottom = tree.style.margin?.bottom?.value ?? 0;
695+
final lastChildBottom = tree.children.last.style.margin?.bottom?.value ?? 0;
696696
final newOuterMarginBottom = max(parentBottom, lastChildBottom);
697697

698698
// Set the parent's margin
699699
if (tree.style.margin == null) {
700-
tree.style.margin = EdgeInsets.only(bottom: newOuterMarginBottom);
700+
tree.style.margin = Margins.only(bottom: newOuterMarginBottom);
701701
} else {
702702
tree.style.margin =
703-
tree.style.margin!.copyWith(bottom: newOuterMarginBottom);
703+
tree.style.margin!.copyWithEdge(bottom: newOuterMarginBottom);
704704
}
705705

706706
// And remove the child's margin
707707
if (tree.children.last.style.margin == null) {
708-
tree.children.last.style.margin = EdgeInsets.zero;
708+
tree.children.last.style.margin = Margins.zero;
709709
} else {
710710
tree.children.last.style.margin =
711-
tree.children.last.style.margin!.copyWith(bottom: 0);
711+
tree.children.last.style.margin!.copyWithEdge(bottom: 0);
712712
}
713713
}
714714

715715
// Handle case (2) from above.
716716
if (tree.children.length > 1) {
717717
for (int i = 1; i < tree.children.length; i++) {
718718
final previousSiblingBottom =
719-
tree.children[i - 1].style.margin?.bottom ?? 0;
720-
final thisTop = tree.children[i].style.margin?.top ?? 0;
719+
tree.children[i - 1].style.margin?.bottom?.value ?? 0;
720+
final thisTop = tree.children[i].style.margin?.top?.value ?? 0;
721721
final newInternalMargin = max(previousSiblingBottom, thisTop) / 2;
722722

723723
if (tree.children[i - 1].style.margin == null) {
724724
tree.children[i - 1].style.margin =
725-
EdgeInsets.only(bottom: newInternalMargin);
725+
Margins.only(bottom: newInternalMargin);
726726
} else {
727727
tree.children[i - 1].style.margin = tree.children[i - 1].style.margin!
728-
.copyWith(bottom: newInternalMargin);
728+
.copyWithEdge(bottom: newInternalMargin);
729729
}
730730

731731
if (tree.children[i].style.margin == null) {
732732
tree.children[i].style.margin =
733-
EdgeInsets.only(top: newInternalMargin);
733+
Margins.only(top: newInternalMargin);
734734
} else {
735735
tree.children[i].style.margin =
736-
tree.children[i].style.margin!.copyWith(top: newInternalMargin);
736+
tree.children[i].style.margin!.copyWithEdge(top: newInternalMargin);
737737
}
738738
}
739739
}
@@ -847,16 +847,23 @@ class ContainerSpan extends StatelessWidget {
847847

848848
@override
849849
Widget build(BuildContext _) {
850-
return Container(
850+
851+
// Elements that are inline should ignore margin: auto for alignment.
852+
var alignment = shrinkWrap ? null : style.alignment;
853+
if(style.display == Display.BLOCK) {
854+
alignment = style.margin?.alignment ?? alignment;
855+
}
856+
857+
Widget container = Container(
851858
decoration: BoxDecoration(
852859
border: style.border,
853860
color: style.backgroundColor,
854861
),
855862
height: style.height,
856863
width: style.width,
857864
padding: style.padding?.nonNegative,
858-
margin: style.margin?.nonNegative,
859-
alignment: shrinkWrap ? null : style.alignment,
865+
margin: style.margin?.asInsets.nonNegative,
866+
alignment: alignment,
860867
child: child ??
861868
StyledText(
862869
textSpan: TextSpan(
@@ -867,6 +874,8 @@ class ContainerSpan extends StatelessWidget {
867874
renderContext: newContext,
868875
),
869876
);
877+
878+
return container;
870879
}
871880
}
872881

lib/src/css_parser.dart

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -244,30 +244,31 @@ Style declarationsToStyle(Map<String, List<css.Expression>> declarations) {
244244
&& !(element is css.EmTerm)
245245
&& !(element is css.RemTerm)
246246
&& !(element is css.NumberTerm)
247+
&& !(element.text == 'auto')
247248
);
248-
List<double?> margin = ExpressionMapping.expressionToPadding(marginLengths);
249-
style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
250-
left: margin[0],
251-
right: margin[1],
252-
top: margin[2],
253-
bottom: margin[3],
249+
Margins margin = ExpressionMapping.expressionToMargins(marginLengths);
250+
style.margin = (style.margin ?? Margins.all(0)).copyWith(
251+
left: margin.left,
252+
right: margin.right,
253+
top: margin.top,
254+
bottom: margin.bottom,
254255
);
255256
break;
256257
case 'margin-left':
257-
style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
258-
left: ExpressionMapping.expressionToPaddingLength(value.first));
258+
style.margin = (style.margin ?? Margins.zero).copyWith(
259+
left: ExpressionMapping.expressionToMargin(value.first));
259260
break;
260261
case 'margin-right':
261-
style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
262-
right: ExpressionMapping.expressionToPaddingLength(value.first));
262+
style.margin = (style.margin ?? Margins.zero).copyWith(
263+
right: ExpressionMapping.expressionToMargin(value.first));
263264
break;
264265
case 'margin-top':
265-
style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
266-
top: ExpressionMapping.expressionToPaddingLength(value.first));
266+
style.margin = (style.margin ?? Margins.zero).copyWith(
267+
top: ExpressionMapping.expressionToMargin(value.first));
267268
break;
268269
case 'margin-bottom':
269-
style.margin = (style.margin ?? EdgeInsets.zero).copyWith(
270-
bottom: ExpressionMapping.expressionToPaddingLength(value.first));
270+
style.margin = (style.margin ?? Margins.zero).copyWith(
271+
bottom: ExpressionMapping.expressionToMargin(value.first));
271272
break;
272273
case 'padding':
273274
List<css.LiteralTerm>? paddingLengths = value.whereType<css.LiteralTerm>().toList();
@@ -748,6 +749,45 @@ class ExpressionMapping {
748749
return null;
749750
}
750751

752+
static Margin? expressionToMargin(css.Expression value) {
753+
if ((value is css.LiteralTerm) && value.text == 'auto') {
754+
return AutoMargin();
755+
} else {
756+
return InsetMargin(expressionToPaddingLength(value) ?? 0);
757+
}
758+
}
759+
760+
static Margins expressionToMargins(List<css.Expression>? lengths) {
761+
Margin? left;
762+
Margin? right;
763+
Margin? top;
764+
Margin? bottom;
765+
if (lengths != null && lengths.isNotEmpty) {
766+
top = expressionToMargin(lengths.first);
767+
if (lengths.length == 4) {
768+
right = expressionToMargin(lengths[1]);
769+
bottom = expressionToMargin(lengths[2]);
770+
left = expressionToMargin(lengths.last);
771+
}
772+
if (lengths.length == 3) {
773+
left = expressionToMargin(lengths[1]);
774+
right = expressionToMargin(lengths[1]);
775+
bottom = expressionToMargin(lengths.last);
776+
}
777+
if (lengths.length == 2) {
778+
bottom = expressionToMargin(lengths.first);
779+
left = expressionToMargin(lengths.last);
780+
right = expressionToMargin(lengths.last);
781+
}
782+
if (lengths.length == 1) {
783+
bottom = expressionToMargin(lengths.first);
784+
left = expressionToMargin(lengths.first);
785+
right = expressionToMargin(lengths.first);
786+
}
787+
}
788+
return Margins(left: left, right: right, top: top, bottom: bottom);
789+
}
790+
751791
static List<double?> expressionToPadding(List<css.Expression>? lengths) {
752792
double? left;
753793
double? right;

lib/src/styled_element.dart

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -102,19 +102,19 @@ StyledElement parseStyledElement(
102102
//TODO(Sub6Resources) this is a workaround for collapsing margins. Remove.
103103
if (element.parent!.localName == "blockquote") {
104104
styledElement.style = Style(
105-
margin: const EdgeInsets.only(left: 40.0, right: 40.0, bottom: 14.0),
105+
margin: Margins.only(left: 40.0, right: 40.0, bottom: 14.0),
106106
display: Display.BLOCK,
107107
);
108108
} else {
109109
styledElement.style = Style(
110-
margin: const EdgeInsets.symmetric(horizontal: 40.0, vertical: 14.0),
110+
margin: Margins.symmetric(horizontal: 40.0, vertical: 14.0),
111111
display: Display.BLOCK,
112112
);
113113
}
114114
break;
115115
case "body":
116116
styledElement.style = Style(
117-
margin: EdgeInsets.all(8.0),
117+
margin: Margins.all(8.0),
118118
display: Display.BLOCK,
119119
);
120120
break;
@@ -134,7 +134,7 @@ StyledElement parseStyledElement(
134134
break;
135135
case "dd":
136136
styledElement.style = Style(
137-
margin: EdgeInsets.only(left: 40.0),
137+
margin: Margins.only(left: 40.0),
138138
display: Display.BLOCK,
139139
);
140140
break;
@@ -148,13 +148,13 @@ StyledElement parseStyledElement(
148148
continue italics;
149149
case "div":
150150
styledElement.style = Style(
151-
margin: EdgeInsets.all(0),
151+
margin: Margins.all(0),
152152
display: Display.BLOCK,
153153
);
154154
break;
155155
case "dl":
156156
styledElement.style = Style(
157-
margin: EdgeInsets.symmetric(vertical: 14.0),
157+
margin: Margins.symmetric(vertical: 14.0),
158158
display: Display.BLOCK,
159159
);
160160
break;
@@ -172,7 +172,7 @@ StyledElement parseStyledElement(
172172
break;
173173
case "figure":
174174
styledElement.style = Style(
175-
margin: EdgeInsets.symmetric(vertical: 14.0, horizontal: 40.0),
175+
margin: Margins.symmetric(vertical: 14.0, horizontal: 40.0),
176176
display: Display.BLOCK,
177177
);
178178
break;
@@ -196,47 +196,47 @@ StyledElement parseStyledElement(
196196
styledElement.style = Style(
197197
fontSize: FontSize.xxLarge,
198198
fontWeight: FontWeight.bold,
199-
margin: EdgeInsets.symmetric(vertical: 18.67),
199+
margin: Margins.symmetric(vertical: 18.67),
200200
display: Display.BLOCK,
201201
);
202202
break;
203203
case "h2":
204204
styledElement.style = Style(
205205
fontSize: FontSize.xLarge,
206206
fontWeight: FontWeight.bold,
207-
margin: EdgeInsets.symmetric(vertical: 17.5),
207+
margin: Margins.symmetric(vertical: 17.5),
208208
display: Display.BLOCK,
209209
);
210210
break;
211211
case "h3":
212212
styledElement.style = Style(
213213
fontSize: FontSize(16.38),
214214
fontWeight: FontWeight.bold,
215-
margin: EdgeInsets.symmetric(vertical: 16.5),
215+
margin: Margins.symmetric(vertical: 16.5),
216216
display: Display.BLOCK,
217217
);
218218
break;
219219
case "h4":
220220
styledElement.style = Style(
221221
fontSize: FontSize.medium,
222222
fontWeight: FontWeight.bold,
223-
margin: EdgeInsets.symmetric(vertical: 18.5),
223+
margin: Margins.symmetric(vertical: 18.5),
224224
display: Display.BLOCK,
225225
);
226226
break;
227227
case "h5":
228228
styledElement.style = Style(
229229
fontSize: FontSize(11.62),
230230
fontWeight: FontWeight.bold,
231-
margin: EdgeInsets.symmetric(vertical: 19.25),
231+
margin: Margins.symmetric(vertical: 19.25),
232232
display: Display.BLOCK,
233233
);
234234
break;
235235
case "h6":
236236
styledElement.style = Style(
237237
fontSize: FontSize(9.38),
238238
fontWeight: FontWeight.bold,
239-
margin: EdgeInsets.symmetric(vertical: 22),
239+
margin: Margins.symmetric(vertical: 22),
240240
display: Display.BLOCK,
241241
);
242242
break;
@@ -247,7 +247,7 @@ StyledElement parseStyledElement(
247247
break;
248248
case "hr":
249249
styledElement.style = Style(
250-
margin: EdgeInsets.symmetric(vertical: 7.0),
250+
margin: Margins.symmetric(vertical: 7.0),
251251
width: double.infinity,
252252
height: 1,
253253
backgroundColor: Colors.black,
@@ -318,14 +318,14 @@ StyledElement parseStyledElement(
318318
break;
319319
case "p":
320320
styledElement.style = Style(
321-
margin: EdgeInsets.symmetric(vertical: 14.0),
321+
margin: Margins.symmetric(vertical: 14.0),
322322
display: Display.BLOCK,
323323
);
324324
break;
325325
case "pre":
326326
styledElement.style = Style(
327327
fontFamily: 'monospace',
328-
margin: EdgeInsets.symmetric(vertical: 14.0),
328+
margin: Margins.symmetric(vertical: 14.0),
329329
whiteSpace: WhiteSpace.PRE,
330330
display: Display.BLOCK,
331331
);

0 commit comments

Comments
 (0)