@@ -27,6 +27,8 @@ A Flutter widget for rendering HTML and CSS as Flutter widgets.
2727
2828- [ Currently Supported CSS Attributes] ( #currently-supported-css-attributes )
2929
30+ - [ Currently Supported Inline CSS Attributes] ( #currently-supported-inline-css-attributes )
31+
3032- [ Why flutter_html?] ( #why-this-package )
3133
3234- [ API Reference] ( #api-reference )
@@ -106,6 +108,13 @@ Add the following to your `pubspec.yaml` file:
106108| ` padding ` | ` margin ` | ` text-align ` | ` text-decoration ` | ` text-decoration-color ` | ` text-decoration-style ` | ` text-decoration-thickness ` |
107109| ` text-shadow ` | ` vertical-align ` | ` white-space ` | ` width ` | ` word-spacing ` | | |
108110
111+ ## Currently Supported Inline CSS Attributes:
112+ | | | | | | | |
113+ | ------------------| --------| ------------| ----------| --------------| ------------------------| ------------|
114+ | ` background-color ` | ` border ` | ` color ` | ` direction ` | ` display ` | ` font-family ` | ` font-feature-settings ` |
115+ | ` font-size ` | ` font-style ` | ` font-weight ` | ` line-height ` | ` list-style-type ` | ` list-style-position ` | ` padding ` |
116+ | ` margin ` | ` text-align ` | ` text-decoration ` | ` text-decoration-color ` | ` text-decoration-style ` | ` text-shadow ` | |
117+
109118Don't see a tag or attribute you need? File a feature request or contribute to the project!
110119
111120## Why this package?
@@ -171,7 +180,7 @@ Widget html = Html(
171180 data: """<p>
172181 Linking to <a href='https://github.com'>websites</a> has never been easier.
173182 </p>""",
174- onLinkTap: (String url) {
183+ onLinkTap: (String? url, RenderContext context, Map<String, String> attributes, dom.Element? element ) {
175184 //open URL in webview, or launch URL in browser, or any other logic here
176185 }
177186);
@@ -197,10 +206,10 @@ Widget html = Html(
197206 <flutter horizontal></flutter>
198207 """,
199208 customRender: {
200- "bird": (RenderContext context, Widget child, Map<String, String> attributes, _ ) {
209+ "bird": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element ) {
201210 return TextSpan(text: "🐦");
202211 },
203- "flutter": (RenderContext context, Widget child, Map<String, String> attributes, _ ) {
212+ "flutter": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element ) {
204213 return FlutterLogo(
205214 style: (attributes['horizontal'] != null)
206215 ? FlutterLogoStyle.horizontal
@@ -227,26 +236,25 @@ Widget html = Html(
227236 <iframe src="https://www.youtube.com/embed/tgbNymZ7vqY"></iframe>
228237 """,
229238 customRender: {
230- "iframe": (RenderContext context, Widget child, Map<String, String> attributes, _ ) {
239+ "iframe": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element ) {
231240 if (attributes != null) {
232241 double width = double.tryParse(attributes['width'] ?? "");
233242 double height = double.tryParse(attributes['height'] ?? "");
234- print(attributes['src']);
235243 return Container(
236244 width: width ?? (height ?? 150) * 2,
237245 height: height ?? (width ?? 300) / 2,
238246 child: WebView(
239- initialUrl: attributes['src'],
247+ initialUrl: attributes['src'] ?? "about:blank" ,
240248 javascriptMode: JavascriptMode.unrestricted,
241249 //no need for scrolling gesture recognizers on embedded youtube, so set gestureRecognizers null
242250 //on other iframe content scrolling might be necessary, so use VerticalDragGestureRecognizer
243- gestureRecognizers: attributes['src'].contains("youtube.com/embed") ? null : [
251+ gestureRecognizers: attributes['src'] != null && attributes['src']! .contains("youtube.com/embed") ? null : [
244252 Factory(() => VerticalDragGestureRecognizer())
245253 ].toSet(),
246254 navigationDelegate: (NavigationRequest request) async {
247255 //no need to load any url besides the embedded youtube url when displaying embedded youtube, so prevent url loading
248256 //on other iframe content allow all url loading
249- if (attributes['src'].contains("youtube.com/embed")) {
257+ if (attributes['src'] != null && attributes['src']! .contains("youtube.com/embed")) {
250258 if (!request.url.contains("youtube.com/embed")) {
251259 return NavigationDecision.prevent;
252260 } else {
@@ -293,7 +301,7 @@ A function that defines what the widget should do when an image is tapped.
293301``` dart
294302Widget html = Html(
295303 data: """<img alt='Google' src='https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png' />""",
296- onImageTap: (String url) {
304+ onImageTap: (String? url, RenderContext context, Map<String, String> attributes, dom.Element? element ) {
297305 //open image in webview, or launch image in browser, or any other logic here
298306 }
299307);
@@ -432,8 +440,9 @@ A typical usage would look something like this:
432440
433441``` dart
434442ImageSourceMatcher base64UriMatcher() => (attributes, element) =>
435- attributes["src"].startsWith("data:image") &&
436- attributes["src"].contains("base64,");
443+ attributes["src"] != null &&
444+ attributes["src"]!.startsWith("data:image") &&
445+ attributes["src"]!.contains("base64,");
437446```
438447
439448In 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 +457,10 @@ ImageSourceMatcher networkSourceMatcher({
448457 String extension: "your extension",
449458}) =>
450459 (attributes, element) {
451- final src = Uri.parse(attributes["src"]);
460+ final src = Uri.parse(attributes["src"] ?? "about:blank" );
452461 return schemas.contains(src.scheme) &&
453- ( domains == null || domains .contains(src.host) ) &&
454- (extension == null || src.path.endsWith(".$extension") );
462+ domains.contains(src.host) &&
463+ src.path.endsWith(".$extension");
455464 };
456465```
457466
@@ -465,7 +474,8 @@ A typical usage might look like this:
465474
466475``` dart
467476ImageRender base64ImageRender() => (context, attributes, element) {
468- 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");
469479 return Image.memory(
470480 decodedImage,
471481 );
@@ -485,7 +495,7 @@ ImageRender networkImageRender({
485495}) =>
486496 (context, attributes, element) {
487497 return Image.network(
488- attributes["src"],
498+ attributes["src"] ?? "about:blank" ,
489499 headers: headers,
490500 width: width,
491501 height: height,
@@ -521,10 +531,10 @@ Widget html = Html(
521531 },
522532 networkSourceMatcher(): networkImageRender(
523533 headers: {"Custom-Header": "some-value"},
524- altWidget: (alt) => Text(alt),
534+ altWidget: (alt) => Text(alt ?? "" ),
525535 loadingWidget: () => Text("Loading..."),
526536 ),
527- (attr, _) => attr["src"] != null && attr["src"].startsWith("/wiki"):
537+ (attr, _) => attr["src"] != null && attr["src"]! .startsWith("/wiki"):
528538 networkImageRender(
529539 mapUrl: (url) => "https://upload.wikimedia.org" + url),
530540 },
@@ -538,16 +548,17 @@ When an image with URL `flutter.dev` is detected, rather than displaying the ima
5385482 . Creating your own renders:
539549``` dart
540550ImageSourceMatcher classAndIdMatcher({String classToMatch, String idToMatch}) => (attributes, element) =>
541- attributes["class"].contains(classToMatch) ||
542- attributes["id"].contains(idToMatch);
551+ attributes["class"] != null && attributes["id"] != null &&
552+ (attributes["class"]!.contains(classToMatch) ||
553+ attributes["id"]!.contains(idToMatch));
543554
544555ImageRender classAndIdRender({String classToMatch, String idToMatch}) => (context, attributes, element) {
545- if (attributes["class"].contains(classToMatch)) {
546- return Image.asset(attributes["src"]);
556+ if (attributes["class"] != null && attributes["class"]! .contains(classToMatch)) {
557+ return Image.asset(attributes["src"] ?? "about:blank" );
547558 } else {
548559 return Image.network(
549- attributes["src"],
550- semanticLabel: attributes["longdesc"],
560+ attributes["src"] ?? "about:blank" ,
561+ semanticLabel: attributes["longdesc"] ?? "" ,
551562 width: attributes["width"],
552563 height: attributes["height"],
553564 color: context.style.color,
@@ -609,9 +620,7 @@ You can set the `navigationDelegate` of the webview with the `navigationDelegate
609620
610621### Audio
611622
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.
623+ This package renders audio elements using the [ ` chewie_audio ` ] ( https://pub.dev/packages/chewie_audio ) plugin.
615624
616625The package considers the attributes ` controls ` , ` loop ` , ` src ` , ` autoplay ` , ` width ` , and ` muted ` when rendering the audio widget.
617626
0 commit comments