@@ -37,43 +37,27 @@ A Flutter widget for rendering HTML and CSS as Flutter widgets.
37
37
38
38
- [ Data] ( #data )
39
39
40
- - [ Example] ( #example-usage---data )
41
-
42
40
- [ onLinkTap] ( #onlinktap )
43
41
44
- - [ Example] ( #example-usage---onlinktap )
45
-
46
42
- [ customRender] ( #customrender )
47
43
48
- - [ Example] ( #example-usages---customrender )
49
-
50
44
- [ onImageError] ( #onimageerror )
51
45
52
- - [ Example] ( #example-usage---onimageerror )
53
-
54
46
- [ onImageTap] ( #onimagetap )
55
47
56
- - [ Example] ( #example-usage---onimagetap )
57
-
58
48
- [ blacklistedElements] ( #blacklistedelements )
59
49
60
- - [ Example] ( #example-usage---blacklistedelements )
61
-
62
50
- [ style] ( #style )
63
51
64
- - [ Example] ( #example-usage---style )
65
-
66
52
- [ navigationDelegateForIframe] ( #navigationdelegateforiframe )
67
53
68
- - [ Example] ( #example-usage---navigationdelegateforiframe )
69
-
70
54
- [ customImageRender] ( #customimagerender )
71
55
72
56
- [ typedef ImageSourceMatcher (with examples)] ( #typedef-imagesourcematcher )
73
57
74
58
- [ typedef ImageRender (with examples)] ( #typedef-imagerender )
75
59
76
- - [ Examples ] ( #example-usages---customimagerender )
60
+ - [ Extended examples ] ( #example-usages---customimagerender )
77
61
78
62
- [ Rendering Reference] ( #rendering-reference )
79
63
@@ -100,7 +84,7 @@ A Flutter widget for rendering HTML and CSS as Flutter widgets.
100
84
Add the following to your ` pubspec.yaml ` file:
101
85
102
86
dependencies:
103
- flutter_html: ^1.2 .0
87
+ flutter_html: ^1.3 .0
104
88
105
89
## Currently Supported HTML Tags:
106
90
| | | | | | | | | | | |
@@ -196,7 +180,7 @@ Widget html = Html(
196
180
data: """<p>
197
181
Linking to <a href='https://github.com'>websites</a> has never been easier.
198
182
</p>""",
199
- onLinkTap: (String url) {
183
+ onLinkTap: (String? url, RenderContext context, Map<String, String> attributes, dom.Element? element ) {
200
184
//open URL in webview, or launch URL in browser, or any other logic here
201
185
}
202
186
);
@@ -222,10 +206,10 @@ Widget html = Html(
222
206
<flutter horizontal></flutter>
223
207
""",
224
208
customRender: {
225
- "bird": (RenderContext context, Widget child, Map<String, String> attributes, _ ) {
209
+ "bird": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element ) {
226
210
return TextSpan(text: "🐦");
227
211
},
228
- "flutter": (RenderContext context, Widget child, Map<String, String> attributes, _ ) {
212
+ "flutter": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element ) {
229
213
return FlutterLogo(
230
214
style: (attributes['horizontal'] != null)
231
215
? FlutterLogoStyle.horizontal
@@ -252,26 +236,25 @@ Widget html = Html(
252
236
<iframe src="https://www.youtube.com/embed/tgbNymZ7vqY"></iframe>
253
237
""",
254
238
customRender: {
255
- "iframe": (RenderContext context, Widget child, Map<String, String> attributes, _ ) {
239
+ "iframe": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element ) {
256
240
if (attributes != null) {
257
241
double width = double.tryParse(attributes['width'] ?? "");
258
242
double height = double.tryParse(attributes['height'] ?? "");
259
- print(attributes['src']);
260
243
return Container(
261
244
width: width ?? (height ?? 150) * 2,
262
245
height: height ?? (width ?? 300) / 2,
263
246
child: WebView(
264
- initialUrl: attributes['src'],
247
+ initialUrl: attributes['src'] ?? "about:blank" ,
265
248
javascriptMode: JavascriptMode.unrestricted,
266
249
//no need for scrolling gesture recognizers on embedded youtube, so set gestureRecognizers null
267
250
//on other iframe content scrolling might be necessary, so use VerticalDragGestureRecognizer
268
- gestureRecognizers: attributes['src'].contains("youtube.com/embed") ? null : [
251
+ gestureRecognizers: attributes['src'] != null && attributes['src']! .contains("youtube.com/embed") ? null : [
269
252
Factory(() => VerticalDragGestureRecognizer())
270
253
].toSet(),
271
254
navigationDelegate: (NavigationRequest request) async {
272
255
//no need to load any url besides the embedded youtube url when displaying embedded youtube, so prevent url loading
273
256
//on other iframe content allow all url loading
274
- if (attributes['src'].contains("youtube.com/embed")) {
257
+ if (attributes['src'] != null && attributes['src']! .contains("youtube.com/embed")) {
275
258
if (!request.url.contains("youtube.com/embed")) {
276
259
return NavigationDecision.prevent;
277
260
} else {
@@ -318,7 +301,7 @@ A function that defines what the widget should do when an image is tapped.
318
301
``` dart
319
302
Widget html = Html(
320
303
data: """<img alt='Google' src='https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png' />""",
321
- onImageTap: (String url) {
304
+ onImageTap: (String? url, RenderContext context, Map<String, String> attributes, dom.Element? element ) {
322
305
//open image in webview, or launch image in browser, or any other logic here
323
306
}
324
307
);
@@ -457,8 +440,9 @@ A typical usage would look something like this:
457
440
458
441
``` dart
459
442
ImageSourceMatcher base64UriMatcher() => (attributes, element) =>
460
- attributes["src"].startsWith("data:image") &&
461
- attributes["src"].contains("base64,");
443
+ attributes["src"] != null &&
444
+ attributes["src"]!.startsWith("data:image") &&
445
+ attributes["src"]!.contains("base64,");
462
446
```
463
447
464
448
In the above example, the matcher checks whether the image's ` src ` either starts with "data: image " or contains "base64,", since these indicate an image in base64 format.
@@ -473,10 +457,10 @@ ImageSourceMatcher networkSourceMatcher({
473
457
String extension: "your extension",
474
458
}) =>
475
459
(attributes, element) {
476
- final src = Uri.parse(attributes["src"]);
460
+ final src = Uri.parse(attributes["src"] ?? "about:blank" );
477
461
return schemas.contains(src.scheme) &&
478
- ( domains == null || domains .contains(src.host) ) &&
479
- (extension == null || src.path.endsWith(".$extension") );
462
+ domains.contains(src.host) &&
463
+ src.path.endsWith(".$extension");
480
464
};
481
465
```
482
466
@@ -490,7 +474,8 @@ A typical usage might look like this:
490
474
491
475
``` dart
492
476
ImageRender base64ImageRender() => (context, attributes, element) {
493
- final decodedImage = base64.decode(attributes["src"].split("base64,")[1].trim());
477
+ final decodedImage = base64.decode(attributes["src"] != null ?
478
+ attributes["src"].split("base64,")[1].trim() : "about:blank");
494
479
return Image.memory(
495
480
decodedImage,
496
481
);
@@ -510,7 +495,7 @@ ImageRender networkImageRender({
510
495
}) =>
511
496
(context, attributes, element) {
512
497
return Image.network(
513
- attributes["src"],
498
+ attributes["src"] ?? "about:blank" ,
514
499
headers: headers,
515
500
width: width,
516
501
height: height,
@@ -546,10 +531,10 @@ Widget html = Html(
546
531
},
547
532
networkSourceMatcher(): networkImageRender(
548
533
headers: {"Custom-Header": "some-value"},
549
- altWidget: (alt) => Text(alt),
534
+ altWidget: (alt) => Text(alt ?? "" ),
550
535
loadingWidget: () => Text("Loading..."),
551
536
),
552
- (attr, _) => attr["src"] != null && attr["src"].startsWith("/wiki"):
537
+ (attr, _) => attr["src"] != null && attr["src"]! .startsWith("/wiki"):
553
538
networkImageRender(
554
539
mapUrl: (url) => "https://upload.wikimedia.org" + url),
555
540
},
@@ -563,16 +548,17 @@ When an image with URL `flutter.dev` is detected, rather than displaying the ima
563
548
2 . Creating your own renders:
564
549
``` dart
565
550
ImageSourceMatcher classAndIdMatcher({String classToMatch, String idToMatch}) => (attributes, element) =>
566
- attributes["class"].contains(classToMatch) ||
567
- attributes["id"].contains(idToMatch);
551
+ attributes["class"] != null && attributes["id"] != null &&
552
+ (attributes["class"]!.contains(classToMatch) ||
553
+ attributes["id"]!.contains(idToMatch));
568
554
569
555
ImageRender classAndIdRender({String classToMatch, String idToMatch}) => (context, attributes, element) {
570
- if (attributes["class"].contains(classToMatch)) {
571
- return Image.asset(attributes["src"]);
556
+ if (attributes["class"] != null && attributes["class"]! .contains(classToMatch)) {
557
+ return Image.asset(attributes["src"] ?? "about:blank" );
572
558
} else {
573
559
return Image.network(
574
- attributes["src"],
575
- semanticLabel: attributes["longdesc"],
560
+ attributes["src"] ?? "about:blank" ,
561
+ semanticLabel: attributes["longdesc"] ?? "" ,
576
562
width: attributes["width"],
577
563
height: attributes["height"],
578
564
color: context.style.color,
@@ -634,9 +620,7 @@ You can set the `navigationDelegate` of the webview with the `navigationDelegate
634
620
635
621
### Audio
636
622
637
- This package renders audio elements using the [ ` chewie_audio ` ] ( https://pub.dev/packages/chewie_audio ) plugin.
638
-
639
- Note: Audio elements currently do not work on iOS due to a bug with ` chewie_audio ` . Once [ #509 ] ( https://github.com/Sub6Resources/flutter_html/pull/509 ) is merged, it will work again.
623
+ This package renders audio elements using the [ ` chewie_audio ` ] ( https://pub.dev/packages/chewie_audio ) plugin.
640
624
641
625
The package considers the attributes ` controls ` , ` loop ` , ` src ` , ` autoplay ` , ` width ` , and ` muted ` when rendering the audio widget.
642
626
0 commit comments