@@ -171,7 +171,7 @@ Widget html = Html(
171
171
data: """<p>
172
172
Linking to <a href='https://github.com'>websites</a> has never been easier.
173
173
</p>""",
174
- onLinkTap: (String url) {
174
+ onLinkTap: (String? url, RenderContext context, Map<String, String> attributes, dom.Element? element ) {
175
175
//open URL in webview, or launch URL in browser, or any other logic here
176
176
}
177
177
);
@@ -197,10 +197,10 @@ Widget html = Html(
197
197
<flutter horizontal></flutter>
198
198
""",
199
199
customRender: {
200
- "bird": (RenderContext context, Widget child, Map<String, String> attributes, _ ) {
200
+ "bird": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element ) {
201
201
return TextSpan(text: "🐦");
202
202
},
203
- "flutter": (RenderContext context, Widget child, Map<String, String> attributes, _ ) {
203
+ "flutter": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element ) {
204
204
return FlutterLogo(
205
205
style: (attributes['horizontal'] != null)
206
206
? FlutterLogoStyle.horizontal
@@ -227,26 +227,25 @@ Widget html = Html(
227
227
<iframe src="https://www.youtube.com/embed/tgbNymZ7vqY"></iframe>
228
228
""",
229
229
customRender: {
230
- "iframe": (RenderContext context, Widget child, Map<String, String> attributes, _ ) {
230
+ "iframe": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element ) {
231
231
if (attributes != null) {
232
232
double width = double.tryParse(attributes['width'] ?? "");
233
233
double height = double.tryParse(attributes['height'] ?? "");
234
- print(attributes['src']);
235
234
return Container(
236
235
width: width ?? (height ?? 150) * 2,
237
236
height: height ?? (width ?? 300) / 2,
238
237
child: WebView(
239
- initialUrl: attributes['src'],
238
+ initialUrl: attributes['src'] ?? "about:blank" ,
240
239
javascriptMode: JavascriptMode.unrestricted,
241
240
//no need for scrolling gesture recognizers on embedded youtube, so set gestureRecognizers null
242
241
//on other iframe content scrolling might be necessary, so use VerticalDragGestureRecognizer
243
- gestureRecognizers: attributes['src'].contains("youtube.com/embed") ? null : [
242
+ gestureRecognizers: attributes['src'] != null && attributes['src']! .contains("youtube.com/embed") ? null : [
244
243
Factory(() => VerticalDragGestureRecognizer())
245
244
].toSet(),
246
245
navigationDelegate: (NavigationRequest request) async {
247
246
//no need to load any url besides the embedded youtube url when displaying embedded youtube, so prevent url loading
248
247
//on other iframe content allow all url loading
249
- if (attributes['src'].contains("youtube.com/embed")) {
248
+ if (attributes['src'] != null && attributes['src']! .contains("youtube.com/embed")) {
250
249
if (!request.url.contains("youtube.com/embed")) {
251
250
return NavigationDecision.prevent;
252
251
} else {
@@ -293,7 +292,7 @@ A function that defines what the widget should do when an image is tapped.
293
292
``` dart
294
293
Widget html = Html(
295
294
data: """<img alt='Google' src='https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png' />""",
296
- onImageTap: (String url) {
295
+ onImageTap: (String? url, RenderContext context, Map<String, String> attributes, dom.Element? element ) {
297
296
//open image in webview, or launch image in browser, or any other logic here
298
297
}
299
298
);
@@ -432,8 +431,9 @@ A typical usage would look something like this:
432
431
433
432
``` dart
434
433
ImageSourceMatcher base64UriMatcher() => (attributes, element) =>
435
- attributes["src"].startsWith("data:image") &&
436
- attributes["src"].contains("base64,");
434
+ attributes["src"] != null &&
435
+ attributes["src"]!.startsWith("data:image") &&
436
+ attributes["src"]!.contains("base64,");
437
437
```
438
438
439
439
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.
@@ -448,10 +448,10 @@ ImageSourceMatcher networkSourceMatcher({
448
448
String extension: "your extension",
449
449
}) =>
450
450
(attributes, element) {
451
- final src = Uri.parse(attributes["src"]);
451
+ final src = Uri.parse(attributes["src"] ?? "about:blank" );
452
452
return schemas.contains(src.scheme) &&
453
- ( domains == null || domains .contains(src.host) ) &&
454
- (extension == null || src.path.endsWith(".$extension") );
453
+ domains.contains(src.host) &&
454
+ src.path.endsWith(".$extension");
455
455
};
456
456
```
457
457
@@ -465,7 +465,8 @@ A typical usage might look like this:
465
465
466
466
``` dart
467
467
ImageRender base64ImageRender() => (context, attributes, element) {
468
- final decodedImage = base64.decode(attributes["src"].split("base64,")[1].trim());
468
+ final decodedImage = base64.decode(attributes["src"] != null ?
469
+ attributes["src"].split("base64,")[1].trim() : "about:blank");
469
470
return Image.memory(
470
471
decodedImage,
471
472
);
@@ -485,7 +486,7 @@ ImageRender networkImageRender({
485
486
}) =>
486
487
(context, attributes, element) {
487
488
return Image.network(
488
- attributes["src"],
489
+ attributes["src"] ?? "about:blank" ,
489
490
headers: headers,
490
491
width: width,
491
492
height: height,
@@ -521,10 +522,10 @@ Widget html = Html(
521
522
},
522
523
networkSourceMatcher(): networkImageRender(
523
524
headers: {"Custom-Header": "some-value"},
524
- altWidget: (alt) => Text(alt),
525
+ altWidget: (alt) => Text(alt ?? "" ),
525
526
loadingWidget: () => Text("Loading..."),
526
527
),
527
- (attr, _) => attr["src"] != null && attr["src"].startsWith("/wiki"):
528
+ (attr, _) => attr["src"] != null && attr["src"]! .startsWith("/wiki"):
528
529
networkImageRender(
529
530
mapUrl: (url) => "https://upload.wikimedia.org" + url),
530
531
},
@@ -538,16 +539,17 @@ When an image with URL `flutter.dev` is detected, rather than displaying the ima
538
539
2 . Creating your own renders:
539
540
``` dart
540
541
ImageSourceMatcher classAndIdMatcher({String classToMatch, String idToMatch}) => (attributes, element) =>
541
- attributes["class"].contains(classToMatch) ||
542
- attributes["id"].contains(idToMatch);
542
+ attributes["class"] != null && attributes["id"] != null &&
543
+ (attributes["class"]!.contains(classToMatch) ||
544
+ attributes["id"]!.contains(idToMatch));
543
545
544
546
ImageRender classAndIdRender({String classToMatch, String idToMatch}) => (context, attributes, element) {
545
- if (attributes["class"].contains(classToMatch)) {
546
- return Image.asset(attributes["src"]);
547
+ if (attributes["class"] != null && attributes["class"]! .contains(classToMatch)) {
548
+ return Image.asset(attributes["src"] ?? "about:blank" );
547
549
} else {
548
550
return Image.network(
549
- attributes["src"],
550
- semanticLabel: attributes["longdesc"],
551
+ attributes["src"] ?? "about:blank" ,
552
+ semanticLabel: attributes["longdesc"] ?? "" ,
551
553
width: attributes["width"],
552
554
height: attributes["height"],
553
555
color: context.style.color,
@@ -609,9 +611,7 @@ You can set the `navigationDelegate` of the webview with the `navigationDelegate
609
611
610
612
### Audio
611
613
612
- This package renders audio elements using the [ ` chewie_audio ` ] ( https://pub.dev/packages/chewie_audio ) plugin.
613
-
614
- 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.
614
+ This package renders audio elements using the [ ` chewie_audio ` ] ( https://pub.dev/packages/chewie_audio ) plugin.
615
615
616
616
The package considers the attributes ` controls ` , ` loop ` , ` src ` , ` autoplay ` , ` width ` , and ` muted ` when rendering the audio widget.
617
617
0 commit comments