Skip to content

Commit c5f396d

Browse files
committed
Add support for auto horizontal margins
Allow centering images with auto-margins
1 parent d13ca75 commit c5f396d

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
@@ -650,7 +650,7 @@ class HtmlParser extends StatelessWidget {
650650
if (tree.children.isEmpty) {
651651
// Handle case (4) from above.
652652
if ((tree.style.height ?? 0) == 0) {
653-
tree.style.margin = EdgeInsets.zero;
653+
tree.style.margin = tree.style.margin?.collapse() ?? Margins.zero;
654654
}
655655
return tree;
656656
}
@@ -666,72 +666,72 @@ class HtmlParser extends StatelessWidget {
666666
// Handle case (1) from above.
667667
// Top margins cannot collapse if the element has padding
668668
if ((tree.style.padding?.top ?? 0) == 0) {
669-
final parentTop = tree.style.margin?.top ?? 0;
670-
final firstChildTop = tree.children.first.style.margin?.top ?? 0;
669+
final parentTop = tree.style.margin?.top?.value ?? 0;
670+
final firstChildTop = tree.children.first.style.margin?.top?.value ?? 0;
671671
final newOuterMarginTop = max(parentTop, firstChildTop);
672672

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

680680
// And remove the child's margin
681681
if (tree.children.first.style.margin == null) {
682-
tree.children.first.style.margin = EdgeInsets.zero;
682+
tree.children.first.style.margin = Margins.zero;
683683
} else {
684684
tree.children.first.style.margin =
685-
tree.children.first.style.margin!.copyWith(top: 0);
685+
tree.children.first.style.margin!.copyWithEdge(top: 0);
686686
}
687687
}
688688

689689
// Handle case (3) from above.
690690
// Bottom margins cannot collapse if the element has padding
691691
if ((tree.style.padding?.bottom ?? 0) == 0) {
692-
final parentBottom = tree.style.margin?.bottom ?? 0;
693-
final lastChildBottom = tree.children.last.style.margin?.bottom ?? 0;
692+
final parentBottom = tree.style.margin?.bottom?.value ?? 0;
693+
final lastChildBottom = tree.children.last.style.margin?.bottom?.value ?? 0;
694694
final newOuterMarginBottom = max(parentBottom, lastChildBottom);
695695

696696
// Set the parent's margin
697697
if (tree.style.margin == null) {
698-
tree.style.margin = EdgeInsets.only(bottom: newOuterMarginBottom);
698+
tree.style.margin = Margins.only(bottom: newOuterMarginBottom);
699699
} else {
700700
tree.style.margin =
701-
tree.style.margin!.copyWith(bottom: newOuterMarginBottom);
701+
tree.style.margin!.copyWithEdge(bottom: newOuterMarginBottom);
702702
}
703703

704704
// And remove the child's margin
705705
if (tree.children.last.style.margin == null) {
706-
tree.children.last.style.margin = EdgeInsets.zero;
706+
tree.children.last.style.margin = Margins.zero;
707707
} else {
708708
tree.children.last.style.margin =
709-
tree.children.last.style.margin!.copyWith(bottom: 0);
709+
tree.children.last.style.margin!.copyWithEdge(bottom: 0);
710710
}
711711
}
712712

713713
// Handle case (2) from above.
714714
if (tree.children.length > 1) {
715715
for (int i = 1; i < tree.children.length; i++) {
716716
final previousSiblingBottom =
717-
tree.children[i - 1].style.margin?.bottom ?? 0;
718-
final thisTop = tree.children[i].style.margin?.top ?? 0;
717+
tree.children[i - 1].style.margin?.bottom?.value ?? 0;
718+
final thisTop = tree.children[i].style.margin?.top?.value ?? 0;
719719
final newInternalMargin = max(previousSiblingBottom, thisTop) / 2;
720720

721721
if (tree.children[i - 1].style.margin == null) {
722722
tree.children[i - 1].style.margin =
723-
EdgeInsets.only(bottom: newInternalMargin);
723+
Margins.only(bottom: newInternalMargin);
724724
} else {
725725
tree.children[i - 1].style.margin = tree.children[i - 1].style.margin!
726-
.copyWith(bottom: newInternalMargin);
726+
.copyWithEdge(bottom: newInternalMargin);
727727
}
728728

729729
if (tree.children[i].style.margin == null) {
730730
tree.children[i].style.margin =
731-
EdgeInsets.only(top: newInternalMargin);
731+
Margins.only(top: newInternalMargin);
732732
} else {
733733
tree.children[i].style.margin =
734-
tree.children[i].style.margin!.copyWith(top: newInternalMargin);
734+
tree.children[i].style.margin!.copyWithEdge(top: newInternalMargin);
735735
}
736736
}
737737
}
@@ -840,16 +840,23 @@ class ContainerSpan extends StatelessWidget {
840840

841841
@override
842842
Widget build(BuildContext _) {
843-
return Container(
843+
844+
// Elements that are inline should ignore margin: auto for alignment.
845+
var alignment = shrinkWrap ? null : style.alignment;
846+
if(style.display == Display.BLOCK) {
847+
alignment = style.margin?.alignment ?? alignment;
848+
}
849+
850+
Widget container = Container(
844851
decoration: BoxDecoration(
845852
border: style.border,
846853
color: style.backgroundColor,
847854
),
848855
height: style.height,
849856
width: style.width,
850857
padding: style.padding?.nonNegative,
851-
margin: style.margin?.nonNegative,
852-
alignment: shrinkWrap ? null : style.alignment,
858+
margin: style.margin?.asInsets.nonNegative,
859+
alignment: alignment,
853860
child: child ??
854861
StyledText(
855862
textSpan: TextSpan(
@@ -860,6 +867,8 @@ class ContainerSpan extends StatelessWidget {
860867
renderContext: newContext,
861868
),
862869
);
870+
871+
return container;
863872
}
864873
}
865874

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)