1
+ import 'dart:math' ;
2
+
1
3
import 'package:flutter/material.dart' ;
2
4
import 'package:flutter_html/html_parser.dart' ;
3
5
import 'package:flutter_html/src/html_elements.dart' ;
4
6
import 'package:flutter_html/src/styled_element.dart' ;
5
7
import 'package:flutter_html/style.dart' ;
8
+ import 'package:flutter_layout_grid/flutter_layout_grid.dart' ;
6
9
import 'package:html/dom.dart' as dom;
7
10
8
11
/// A [LayoutElement] is an element that breaks the normal Inline flow of
@@ -28,52 +31,108 @@ class TableLayoutElement extends LayoutElement {
28
31
29
32
@override
30
33
Widget toWidget (RenderContext context) {
31
- final colWidths = children
32
- .where ((c) => c.name == "colgroup" )
33
- .map ((group) {
34
- return group.children.where ((c) => c.name == "col" ).map ((c) {
35
- final widthStr = c.attributes["width" ] ?? "" ;
36
- if (widthStr.endsWith ("%" )) {
37
- final width =
38
- double .tryParse (widthStr.substring (0 , widthStr.length - 1 )) *
39
- 0.01 ;
40
- return FractionColumnWidth (width);
41
- } else {
42
- final width = double .tryParse (widthStr);
43
- return width != null ? FixedColumnWidth (width) : null ;
44
- }
45
- });
46
- })
47
- .expand ((i) => i)
48
- .toList ()
49
- .asMap ();
34
+ final rows = < TableRowLayoutElement > [];
35
+ List <TrackSize > columnSizes;
36
+ for (var child in children) {
37
+ if (child is TableStyleElement ) {
38
+ // Map <col> tags to predetermined column track sizes
39
+ columnSizes = child.children.where ((c) => c.name == "col" ).map ((c) {
40
+ final colWidth = c.attributes["width" ];
41
+ if (colWidth != null && colWidth.endsWith ("%" )) {
42
+ final percentageSize =
43
+ double .tryParse (colWidth.substring (0 , colWidth.length - 1 ));
44
+ return percentageSize != null
45
+ ? FlexibleTrackSize (percentageSize * 0.01 )
46
+ : FlexibleTrackSize (1 );
47
+ } else if (colWidth != null ) {
48
+ final fixedPxSize = double .tryParse (colWidth);
49
+ return fixedPxSize != null
50
+ ? FixedTrackSize (fixedPxSize)
51
+ : FlexibleTrackSize (1 );
52
+ } else {
53
+ return FlexibleTrackSize (1 );
54
+ }
55
+ }).toList (growable: false );
56
+ } else if (child is TableSectionLayoutElement ) {
57
+ rows.addAll (child.children.whereType ());
58
+ } else if (child is TableRowLayoutElement ) {
59
+ rows.add (child);
60
+ }
61
+ }
62
+
63
+ // All table rows have a height intrinsic to their (spanned) contents
64
+ final rowSizes =
65
+ List .generate (rows.length, (_) => IntrinsicContentTrackSize ());
66
+
67
+ // Calculate column bounds
68
+ int columnMax = rows
69
+ .map ((row) => row.children
70
+ .whereType <TableCellElement >()
71
+ .fold (0 , (int value, child) => value + child.colspan))
72
+ .fold (0 , max);
50
73
74
+ final cells = < GridPlacement > [];
75
+ final columnRowOffset = List .generate (columnMax + 1 , (_) => 0 );
76
+ int rowi = 0 ;
77
+ for (var row in rows) {
78
+ int columni = 0 ;
79
+ for (var child in row.children) {
80
+ if (columnRowOffset[columni] > 0 ) {
81
+ columnRowOffset[columni] = columnRowOffset[columni] - 1 ;
82
+ columni++ ;
83
+ }
84
+ if (child is TableCellElement ) {
85
+ cells.add (GridPlacement (
86
+ child: Container (
87
+ width: double .infinity,
88
+ padding: child.style.padding ?? row.style.padding,
89
+ decoration: BoxDecoration (
90
+ color: child.style.backgroundColor ?? row.style.backgroundColor,
91
+ border: child.style.border ?? row.style.border,
92
+ ),
93
+ child: SizedBox .expand (
94
+ child: Container (
95
+ alignment: child.style.alignment ?? style.alignment ??
96
+ Alignment .centerLeft,
97
+ child: StyledText (
98
+ textSpan: context.parser.parseTree (context, child),
99
+ style: child.style,
100
+ ),
101
+ ),
102
+ ),
103
+ ),
104
+ columnStart: columni,
105
+ columnSpan: child.colspan,
106
+ rowStart: rowi,
107
+ rowSpan: child.rowspan,
108
+ ));
109
+ columnRowOffset[columni] = child.rowspan - 1 ;
110
+ columni += child.colspan;
111
+ }
112
+ }
113
+ rowi++ ;
114
+ }
115
+
116
+ final finalColumnSizes =
117
+ columnSizes ?? List .generate (columnMax, (_) => FlexibleTrackSize (1 ));
51
118
return Container (
52
- decoration: BoxDecoration (
53
- color: style.backgroundColor,
54
- border: style.border,
55
- ),
56
- width: style.width,
57
- height: style.height,
58
- child: Table (
59
- columnWidths: colWidths,
60
- children: children
61
- .map ((c) {
62
- if (c is TableSectionLayoutElement ) {
63
- return c.toTableRows (context);
64
- }
65
- return null ;
66
- })
67
- .where ((t) {
68
- return t != null ;
69
- })
70
- .toList ()
71
- .expand ((i) => i)
72
- .toList (),
73
- ));
119
+ decoration: BoxDecoration (
120
+ color: style.backgroundColor,
121
+ border: style.border,
122
+ ),
123
+ width: style.width,
124
+ height: style.height,
125
+ child: LayoutGrid (
126
+ gridFit: GridFit .loose,
127
+ templateColumnSizes: finalColumnSizes,
128
+ templateRowSizes: rowSizes,
129
+ children: cells,
130
+ ),
131
+ );
74
132
}
75
133
}
76
134
135
+
77
136
class TableSectionLayoutElement extends LayoutElement {
78
137
TableSectionLayoutElement ({
79
138
String name,
@@ -82,19 +141,9 @@ class TableSectionLayoutElement extends LayoutElement {
82
141
83
142
@override
84
143
Widget toWidget (RenderContext context) {
144
+ // Not rendered; TableLayoutElement will instead consume its children
85
145
return Container (child: Text ("TABLE SECTION" ));
86
146
}
87
-
88
- List <TableRow > toTableRows (RenderContext context) {
89
- return children.map ((c) {
90
- if (c is TableRowLayoutElement ) {
91
- return c.toTableRow (context);
92
- }
93
- return null ;
94
- }).where ((t) {
95
- return t != null ;
96
- }).toList ();
97
- }
98
147
}
99
148
100
149
class TableRowLayoutElement extends LayoutElement {
@@ -106,35 +155,55 @@ class TableRowLayoutElement extends LayoutElement {
106
155
107
156
@override
108
157
Widget toWidget (RenderContext context) {
158
+ // Not rendered; TableLayoutElement will instead consume its children
109
159
return Container (child: Text ("TABLE ROW" ));
110
160
}
161
+ }
162
+
163
+ class TableCellElement extends StyledElement {
164
+ int colspan = 1 ;
165
+ int rowspan = 1 ;
111
166
112
- TableRow toTableRow (RenderContext context) {
113
- return TableRow (
114
- decoration: BoxDecoration (
115
- border: style.border,
116
- color: style.backgroundColor,
117
- ),
118
- children: children
119
- .map ((c) {
120
- if (c is StyledElement && c.name == 'td' || c.name == 'th' ) {
121
- return TableCell (
122
- child: Container (
123
- padding: c.style.padding,
124
- decoration: BoxDecoration (
125
- color: c.style.backgroundColor,
126
- border: c.style.border,
127
- ),
128
- child: StyledText (
129
- textSpan: context.parser.parseTree (context, c),
130
- style: c.style,
131
- )));
132
- }
133
- return null ;
134
- })
135
- .where ((c) => c != null )
136
- .toList ());
167
+ TableCellElement ({
168
+ String name,
169
+ String elementId,
170
+ List <String > elementClasses,
171
+ @required List <StyledElement > children,
172
+ Style style,
173
+ dom.Element node,
174
+ }) : super (
175
+ name: name,
176
+ elementId: elementId,
177
+ elementClasses: elementClasses,
178
+ children: children,
179
+ style: style,
180
+ node: node) {
181
+ colspan = _parseSpan (this , "colspan" );
182
+ rowspan = _parseSpan (this , "rowspan" );
183
+ }
184
+
185
+ static int _parseSpan (StyledElement element, String attributeName) {
186
+ final spanValue = element.attributes[attributeName];
187
+ return spanValue == null ? 1 : int .tryParse (spanValue) ?? 1 ;
188
+ }
189
+ }
190
+
191
+ TableCellElement parseTableCellElement (dom.Element element,
192
+ List <StyledElement > children,
193
+ ) {
194
+ final cell = TableCellElement (
195
+ name: element.localName,
196
+ elementId: element.id,
197
+ elementClasses: element.classes.toList (),
198
+ children: children,
199
+ node: element,
200
+ );
201
+ if (element.localName == "th" ) {
202
+ cell.style = Style (
203
+ fontWeight: FontWeight .bold,
204
+ );
137
205
}
206
+ return cell;
138
207
}
139
208
140
209
class TableStyleElement extends StyledElement {
@@ -146,9 +215,8 @@ class TableStyleElement extends StyledElement {
146
215
}) : super (name: name, children: children, style: style, node: node);
147
216
}
148
217
149
- TableStyleElement parseTableDefinitionElement (
150
- dom.Element element,
151
- List <StyledElement > children,
218
+ TableStyleElement parseTableDefinitionElement (dom.Element element,
219
+ List <StyledElement > children,
152
220
) {
153
221
switch (element.localName) {
154
222
case "colgroup" :
@@ -163,9 +231,8 @@ TableStyleElement parseTableDefinitionElement(
163
231
}
164
232
}
165
233
166
- LayoutElement parseLayoutElement (
167
- dom.Element element,
168
- List <StyledElement > children,
234
+ LayoutElement parseLayoutElement (dom.Element element,
235
+ List <StyledElement > children,
169
236
) {
170
237
switch (element.localName) {
171
238
case "table" :
0 commit comments