Skip to content

Commit c050862

Browse files
committed
Merge remote-tracking branch 'upstream/master' into bugfix/link-wrapping
# Conflicts: # example/lib/main.dart
2 parents 3fa2409 + 5429f07 commit c050862

File tree

7 files changed

+103
-74
lines changed

7 files changed

+103
-74
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## [1.2.0] - January 14, 2021:
2+
* Support irregular table sizes
3+
* Allow for returning `null` from a customRender function to disable the widget
4+
15
## [1.1.1] - November 22, 2020:
26
* Update dependencies
37

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ A Flutter widget for rendering html and css as Flutter widgets.
1717
Add the following to your `pubspec.yaml` file:
1818

1919
dependencies:
20-
flutter_html: ^1.1.0
20+
flutter_html: ^1.2.0
2121

2222
## Currently Supported HTML Tags:
2323
`a`, `abbr`, `acronym`, `address`, `article`, `aside`, `b`, `bdi`, `bdo`, `big`, `blockquote`, `body`, `br`, `caption`, `cite`, `code`, `data`, `dd`, `del`, `dfn`, `div`, `dl`, `dt`, `em`, `figcaption`, `figure`, `footer`, `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `header`, `hr`, `i`, `img`, `ins`, `kbd`, `li`, `main`, `mark`, `nav`, `noscript`, `ol`, `p`, `pre`, `q`, `rp`, `rt`, `ruby`, `s`, `samp`, `section`, `small`, `span`, `strike`, `strong`, `sub`, `sup`, `table`, `tbody`, `td`, `template`, `tfoot`, `th`, `thead`, `time`, `tr`, `tt`, `u`, `ul`, `var`
@@ -80,4 +80,4 @@ this project has expanded to include support for basic styling as well!.
8080
## Contribution Guide
8181
> Coming soon!
8282
>
83-
> Meanwhile, PRs are always welcome
83+
> Meanwhile, PRs are always welcome

example/lib/main.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ const htmlData = """
6363
<table>
6464
<colgroup>
6565
<col width="50%" />
66-
<col width="25%" />
67-
<col width="25%" />
66+
<col span="2" width="25%" />
6867
</colgroup>
6968
<thead>
7069
<tr><th>One</th><th>Two</th><th>Three</th></tr>
@@ -81,7 +80,7 @@ const htmlData = """
8180
<tr><td>fData</td><td>fData</td><td>fData</td></tr>
8281
</tfoot>
8382
</table>
84-
<h3>Custom Element Support:</h3>
83+
<h3>Custom Element Support (inline: <bird></bird> and as block):</h3>
8584
<flutter></flutter>
8685
<flutter horizontal></flutter>
8786
<h3>SVG support:</h3>
@@ -173,6 +172,9 @@ class _MyHomePageState extends State<MyHomePage> {
173172
"var": Style(fontFamily: 'serif'),
174173
},
175174
customRender: {
175+
"bird": (RenderContext context, Widget child, attributes, _) {
176+
return TextSpan(text: "🐦");
177+
},
176178
"flutter": (RenderContext context, Widget child, attributes, _) {
177179
return FlutterLogo(
178180
style: (attributes['horizontal'] != null)

lib/html_parser.dart

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import 'package:html/parser.dart' as htmlparser;
1616
import 'package:webview_flutter/webview_flutter.dart';
1717

1818
typedef OnTap = void Function(String url);
19-
typedef CustomRender = Widget Function(
19+
typedef CustomRender = dynamic Function(
2020
RenderContext context,
2121
Widget parsedChild,
2222
Map<String, String> attributes,
@@ -244,27 +244,33 @@ class HtmlParser extends StatelessWidget {
244244
);
245245

246246
if (customRender?.containsKey(tree.name) ?? false) {
247-
return WidgetSpan(
248-
child: ContainerSpan(
247+
final render = customRender[tree.name].call(
248+
newContext,
249+
ContainerSpan(
249250
newContext: newContext,
250251
style: tree.style,
251252
shrinkWrap: context.parser.shrinkWrap,
252-
child: customRender[tree.name].call(
253-
newContext,
254-
ContainerSpan(
255-
newContext: newContext,
256-
style: tree.style,
257-
shrinkWrap: context.parser.shrinkWrap,
258-
children: tree.children
259-
?.map((tree) => parseTree(newContext, tree))
260-
?.toList() ??
261-
[],
262-
),
263-
tree.attributes,
264-
tree.element,
265-
),
253+
children: tree.children
254+
?.map((tree) => parseTree(newContext, tree))
255+
?.toList() ??
256+
[],
266257
),
258+
tree.attributes,
259+
tree.element,
267260
);
261+
if (render != null) {
262+
assert(render is InlineSpan || render is Widget);
263+
return render is InlineSpan
264+
? render
265+
: WidgetSpan(
266+
child: ContainerSpan(
267+
newContext: newContext,
268+
style: tree.style,
269+
shrinkWrap: context.parser.shrinkWrap,
270+
child: render,
271+
),
272+
);
273+
}
268274
}
269275

270276
//Return the correct InlineSpan based on the element type.

lib/src/layout_element.dart

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,27 +32,35 @@ class TableLayoutElement extends LayoutElement {
3232
@override
3333
Widget toWidget(RenderContext context) {
3434
final rows = <TableRowLayoutElement>[];
35-
List<TrackSize> columnSizes;
35+
List<TrackSize> columnSizes = <TrackSize>[];
3636
for (var child in children) {
3737
if (child is TableStyleElement) {
3838
// 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);
39+
columnSizes = child.children
40+
.where((c) => c.name == "col")
41+
.map((c) {
42+
final span =
43+
int.parse(c.attributes["span"] ?? "1", onError: (_) => 1);
44+
final colWidth = c.attributes["width"];
45+
return List.generate(span, (index) {
46+
if (colWidth != null && colWidth.endsWith("%")) {
47+
final percentageSize = double.tryParse(
48+
colWidth.substring(0, colWidth.length - 1));
49+
return percentageSize != null
50+
? FlexibleTrackSize(percentageSize * 0.01)
51+
: FlexibleTrackSize(1);
52+
} else if (colWidth != null) {
53+
final fixedPxSize = double.tryParse(colWidth);
54+
return fixedPxSize != null
55+
? FixedTrackSize(fixedPxSize)
56+
: FlexibleTrackSize(1);
57+
} else {
58+
return FlexibleTrackSize(1);
59+
}
60+
});
61+
})
62+
.expand((element) => element)
63+
.toList(growable: false);
5664
} else if (child is TableSectionLayoutElement) {
5765
rows.addAll(child.children.whereType());
5866
} else if (child is TableRowLayoutElement) {
@@ -71,6 +79,7 @@ class TableLayoutElement extends LayoutElement {
7179
.fold(0, (int value, child) => value + child.colspan))
7280
.fold(0, max);
7381

82+
// Place the cells in the rows/columns
7483
final cells = <GridPlacement>[];
7584
final columnRowOffset = List.generate(columnMax + 1, (_) => 0);
7685
int rowi = 0;
@@ -113,8 +122,12 @@ class TableLayoutElement extends LayoutElement {
113122
rowi++;
114123
}
115124

116-
final finalColumnSizes =
117-
columnSizes ?? List.generate(columnMax, (_) => FlexibleTrackSize(1));
125+
// Create column tracks (insofar there were no colgroups that already defined them)
126+
List<TrackSize> finalColumnSizes = (columnSizes ?? <TrackSize>[]).take(
127+
columnMax).toList();
128+
finalColumnSizes += List.generate(
129+
max(0, columnMax - finalColumnSizes.length),
130+
(_) => FlexibleTrackSize(1));
118131
return Container(
119132
decoration: BoxDecoration(
120133
color: style.backgroundColor,

lib/src/replaced_element.dart

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ class ImageContentElement extends ReplacedElement {
120120
return child;
121121
},
122122
);
123+
} else if (src.endsWith(".svg")) {
124+
return SvgPicture.network(src);
123125
} else {
124126
precacheImage(
125127
NetworkImage(src),
@@ -138,27 +140,26 @@ class ImageContentElement extends ReplacedElement {
138140
}
139141
});
140142
image.image.resolve(ImageConfiguration()).addListener(
141-
ImageStreamListener(
142-
(ImageInfo image, bool synchronousCall) {
143-
var myImage = image.image;
144-
Size size = Size(myImage.width.toDouble(), myImage.height.toDouble());
145-
completer.complete(size);
146-
}, onError: (object, stacktrace) {
147-
completer.completeError(object);
148-
}
149-
),
150-
);
143+
ImageStreamListener((ImageInfo image, bool synchronousCall) {
144+
var myImage = image.image;
145+
Size size =
146+
Size(myImage.width.toDouble(), myImage.height.toDouble());
147+
completer.complete(size);
148+
}, onError: (object, stacktrace) {
149+
completer.completeError(object);
150+
}),
151+
);
151152
imageWidget = FutureBuilder<Size>(
152153
future: completer.future,
153154
builder: (BuildContext buildContext, AsyncSnapshot<Size> snapshot) {
154155
if (snapshot.hasData) {
155156
return new Image.network(
156157
src,
157158
width: snapshot.data.width,
158-
height: snapshot.data.height,
159159
frameBuilder: (ctx, child, frame, _) {
160160
if (frame == null) {
161-
return Text(alt ?? "", style: context.style.generateTextStyle());
161+
return Text(alt ?? "",
162+
style: context.style.generateTextStyle());
162163
}
163164
return child;
164165
},
@@ -293,20 +294,23 @@ class VideoContentElement extends ReplacedElement {
293294
Widget toWidget(RenderContext context) {
294295
final double _width = width ?? (height ?? 150) * 2;
295296
final double _height = height ?? (width ?? 300) / 2;
296-
return Container(
297-
child: Chewie(
298-
controller: ChewieController(
299-
videoPlayerController: VideoPlayerController.network(
300-
src.first ?? "",
297+
return AspectRatio(
298+
aspectRatio: _width / _height,
299+
child: Container(
300+
child: Chewie(
301+
controller: ChewieController(
302+
videoPlayerController: VideoPlayerController.network(
303+
src.first ?? "",
304+
),
305+
placeholder: poster != null
306+
? Image.network(poster)
307+
: Container(color: Colors.black),
308+
autoPlay: autoplay,
309+
looping: loop,
310+
showControls: showControls,
311+
autoInitialize: true,
312+
aspectRatio: _width / _height,
301313
),
302-
placeholder: poster != null
303-
? Image.network(poster)
304-
: Container(color: Colors.black),
305-
autoPlay: autoplay,
306-
looping: loop,
307-
showControls: showControls,
308-
autoInitialize: true,
309-
aspectRatio: _width / _height,
310314
),
311315
),
312316
);
@@ -418,12 +422,12 @@ ReplacedElement parseReplacedElement(
418422
);
419423
case "iframe":
420424
return IframeContentElement(
421-
name: "iframe",
422-
src: element.attributes['src'],
423-
width: double.tryParse(element.attributes['width'] ?? ""),
424-
height: double.tryParse(element.attributes['height'] ?? ""),
425-
navigationDelegate: navigationDelegateForIframe,
426-
);
425+
name: "iframe",
426+
src: element.attributes['src'],
427+
width: double.tryParse(element.attributes['width'] ?? ""),
428+
height: double.tryParse(element.attributes['height'] ?? ""),
429+
navigationDelegate: navigationDelegateForIframe,
430+
node: element);
427431
case "img":
428432
return ImageContentElement(
429433
name: "img",

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: flutter_html
22
description: A Flutter widget rendering static HTML and CSS as Flutter widgets.
3-
version: 1.1.1
3+
version: 1.2.0
44
homepage: https://github.com/Sub6Resources/flutter_html
55

66
environment:

0 commit comments

Comments
 (0)