1
1
import 'dart:convert' ;
2
2
3
- import 'package:flutter/material.dart' ;
4
3
import 'package:flutter/gestures.dart' ;
5
- import 'package:html/parser .dart' as parser ;
4
+ import 'package:flutter/material .dart' ;
6
5
import 'package:html/dom.dart' as dom;
6
+ import 'package:html/parser.dart' as parser;
7
7
8
8
typedef CustomRender = Widget Function (dom.Node node, List <Widget > children);
9
9
typedef OnLinkTap = void Function (String url);
@@ -144,6 +144,7 @@ class HtmlRichTextParser extends StatelessWidget {
144
144
this .onLinkTap,
145
145
this .renderNewlines = false ,
146
146
this .html,
147
+ this .onImageError,
147
148
this .linkStyle = const TextStyle (
148
149
decoration: TextDecoration .underline,
149
150
color: Colors .blueAccent,
@@ -156,6 +157,7 @@ class HtmlRichTextParser extends StatelessWidget {
156
157
final onLinkTap;
157
158
final bool renderNewlines;
158
159
final String html;
160
+ final ImageErrorListener onImageError;
159
161
final TextStyle linkStyle;
160
162
161
163
// style elements set a default style
@@ -266,7 +268,8 @@ class HtmlRichTextParser extends StatelessWidget {
266
268
);
267
269
268
270
// ignore the top level "body"
269
- body.nodes.forEach ((dom.Node node) => _parseNode (node, parseContext));
271
+ body.nodes
272
+ .forEach ((dom.Node node) => _parseNode (node, parseContext, context));
270
273
// _parseNode(body, parseContext);
271
274
272
275
// filter out empty widgets
@@ -303,7 +306,8 @@ class HtmlRichTextParser extends StatelessWidget {
303
306
// function can add child nodes to the parent if it should
304
307
//
305
308
// each iteration creates a new parseContext as a copy of the previous one if it needs to
306
- void _parseNode (dom.Node node, ParseContext parseContext) {
309
+ void _parseNode (
310
+ dom.Node node, ParseContext parseContext, BuildContext buildContext) {
307
311
// TEXT ONLY NODES
308
312
// a text only node is a child of a tag with no inner html
309
313
if (node is dom.Text ) {
@@ -624,9 +628,23 @@ class HtmlRichTextParser extends StatelessWidget {
624
628
if (node.attributes['src' ] != null ) {
625
629
if (node.attributes['src' ].startsWith ("data:image" ) &&
626
630
node.attributes['src' ].contains ("base64," )) {
631
+ precacheImage (
632
+ MemoryImage (
633
+ base64.decode (
634
+ node.attributes['src' ].split ("base64," )[1 ].trim (),
635
+ ),
636
+ ),
637
+ buildContext,
638
+ onError: onImageError,
639
+ );
627
640
parseContext.rootWidgetList.add (Image .memory (base64.decode (
628
641
node.attributes['src' ].split ("base64," )[1 ].trim ())));
629
642
} else {
643
+ precacheImage (
644
+ NetworkImage (node.attributes['src' ]),
645
+ buildContext,
646
+ onError: onImageError,
647
+ );
630
648
parseContext.rootWidgetList
631
649
.add (Image .network (node.attributes['src' ]));
632
650
}
@@ -743,7 +761,7 @@ class HtmlRichTextParser extends StatelessWidget {
743
761
}
744
762
745
763
node.nodes.forEach ((dom.Node childNode) {
746
- _parseNode (childNode, nextContext);
764
+ _parseNode (childNode, nextContext, buildContext );
747
765
});
748
766
}
749
767
}
@@ -817,6 +835,7 @@ class HtmlOldParser extends StatelessWidget {
817
835
this .customRender,
818
836
this .blockSpacing,
819
837
this .html,
838
+ this .onImageError,
820
839
this .linkStyle = const TextStyle (
821
840
decoration: TextDecoration .underline,
822
841
color: Colors .blueAccent,
@@ -829,6 +848,7 @@ class HtmlOldParser extends StatelessWidget {
829
848
final CustomRender customRender;
830
849
final double blockSpacing;
831
850
final String html;
851
+ final ImageErrorListener onImageError;
832
852
final TextStyle linkStyle;
833
853
834
854
static const _supportedElements = [
@@ -1291,24 +1311,39 @@ class HtmlOldParser extends StatelessWidget {
1291
1311
),
1292
1312
);
1293
1313
case "img" :
1294
- if (node.attributes['src' ] != null ) {
1295
- if (node.attributes['src' ].startsWith ("data:image" ) &&
1296
- node.attributes['src' ].contains ("base64," )) {
1297
- return Image .memory (base64
1298
- .decode (node.attributes['src' ].split ("base64," )[1 ].trim ()));
1299
- }
1300
- return Image .network (node.attributes['src' ]);
1301
- } else if (node.attributes['alt' ] != null ) {
1302
- //Temp fix for https://github.com/flutter/flutter/issues/736
1303
- if (node.attributes['alt' ].endsWith (" " )) {
1304
- return Container (
1305
- padding: EdgeInsets .only (right: 2.0 ),
1306
- child: Text (node.attributes['alt' ]));
1307
- } else {
1308
- return Text (node.attributes['alt' ]);
1309
- }
1310
- }
1311
- return Container ();
1314
+ return Builder (
1315
+ builder: (BuildContext context) {
1316
+ if (node.attributes['src' ] != null ) {
1317
+ if (node.attributes['src' ].startsWith ("data:image" ) &&
1318
+ node.attributes['src' ].contains ("base64," )) {
1319
+ precacheImage (
1320
+ MemoryImage (base64.decode (
1321
+ node.attributes['src' ].split ("base64," )[1 ].trim ())),
1322
+ context,
1323
+ onError: onImageError,
1324
+ );
1325
+ return Image .memory (base64.decode (
1326
+ node.attributes['src' ].split ("base64," )[1 ].trim ()));
1327
+ }
1328
+ precacheImage (
1329
+ NetworkImage (node.attributes['src' ]),
1330
+ context,
1331
+ onError: onImageError,
1332
+ );
1333
+ return Image .network (node.attributes['src' ]);
1334
+ } else if (node.attributes['alt' ] != null ) {
1335
+ //Temp fix for https://github.com/flutter/flutter/issues/736
1336
+ if (node.attributes['alt' ].endsWith (" " )) {
1337
+ return Container (
1338
+ padding: EdgeInsets .only (right: 2.0 ),
1339
+ child: Text (node.attributes['alt' ]));
1340
+ } else {
1341
+ return Text (node.attributes['alt' ]);
1342
+ }
1343
+ }
1344
+ return Container ();
1345
+ },
1346
+ );
1312
1347
case "ins" :
1313
1348
return DefaultTextStyle .merge (
1314
1349
child: Wrap (
0 commit comments