@@ -15,9 +15,9 @@ CustomRenderMatcher tagMatcher(String tag) => (context) {
15
15
};
16
16
17
17
CustomRenderMatcher blockElementMatcher () => (context) {
18
- return context.tree.style.display == Display .BLOCK &&
19
- (context.tree.children.isNotEmpty || context.tree.element? .localName == "hr" );
20
- };
18
+ return context.tree.style.display == Display .BLOCK &&
19
+ (context.tree.children.isNotEmpty || context.tree.element? .localName == "hr" );
20
+ };
21
21
22
22
CustomRenderMatcher listElementMatcher () => (context) {
23
23
return context.tree.style.display == Display .LIST_ITEM ;
@@ -31,7 +31,7 @@ CustomRenderMatcher dataUriMatcher({String? encoding = 'base64', String? mime})
31
31
if (context.tree.element? .attributes == null
32
32
|| _src (context.tree.element! .attributes.cast ()) == null ) return false ;
33
33
final dataUri = _dataUriFormat.firstMatch (_src (context.tree.element! .attributes.cast ())! );
34
- return dataUri != null &&
34
+ return dataUri != null && dataUri. namedGroup ( 'mime' ) != "image/svg+xml" &&
35
35
(mime == null || dataUri.namedGroup ('mime' ) == mime) &&
36
36
(encoding == null || dataUri.namedGroup ('encoding' ) == ';$encoding ' );
37
37
};
@@ -57,7 +57,8 @@ CustomRenderMatcher networkSourceMatcher({
57
57
CustomRenderMatcher assetUriMatcher () => (context) =>
58
58
context.tree.element? .attributes.cast () != null
59
59
&& _src (context.tree.element! .attributes.cast ()) != null
60
- && _src (context.tree.element! .attributes.cast ())! .startsWith ("asset:" );
60
+ && _src (context.tree.element! .attributes.cast ())! .startsWith ("asset:" )
61
+ && ! _src (context.tree.element! .attributes.cast ())! .endsWith (".svg" );
61
62
62
63
CustomRenderMatcher textContentElementMatcher () => (context) {
63
64
return context.tree is TextContentElement ;
@@ -209,7 +210,7 @@ CustomRender textContentElementRender({String? text}) =>
209
210
CustomRender .inlineSpan (inlineSpan: (context, buildChildren) =>
210
211
TextSpan (text: (text ?? (context.tree as TextContentElement ).text).transformed (context.tree.style.textTransform)));
211
212
212
- CustomRender base64ImageRender () => CustomRender .fromWidget (widget: (context, buildChildren) {
213
+ CustomRender base64ImageRender () => CustomRender .widget (widget: (context, buildChildren) {
213
214
final decodedImage = base64.decode (_src (context.tree.element! .attributes.cast ())! .split ("base64," )[1 ].trim ());
214
215
precacheImage (
215
216
MemoryImage (decodedImage),
@@ -228,9 +229,9 @@ CustomRender base64ImageRender() => CustomRender.fromWidget(widget: (context, bu
228
229
},
229
230
);
230
231
return Builder (
232
+ key: context.key,
231
233
builder: (buildContext) {
232
234
return GestureDetector (
233
- key: context.key,
234
235
child: widget,
235
236
onTap: () {
236
237
if (MultipleTapGestureDetector .of (buildContext) != null ) {
@@ -251,7 +252,7 @@ CustomRender base64ImageRender() => CustomRender.fromWidget(widget: (context, bu
251
252
CustomRender assetImageRender ({
252
253
double ? width,
253
254
double ? height,
254
- }) => CustomRender .fromWidget (widget: (context, buildChildren) {
255
+ }) => CustomRender .widget (widget: (context, buildChildren) {
255
256
final assetPath = _src (context.tree.element! .attributes.cast ())! .replaceFirst ('asset:' , '' );
256
257
final widget = Image .asset (
257
258
assetPath,
@@ -265,9 +266,9 @@ CustomRender assetImageRender({
265
266
},
266
267
);
267
268
return Builder (
269
+ key: context.key,
268
270
builder: (buildContext) {
269
271
return GestureDetector (
270
- key: context.key,
271
272
child: widget,
272
273
onTap: () {
273
274
if (MultipleTapGestureDetector .of (buildContext) != null ) {
@@ -292,62 +293,71 @@ CustomRender networkImageRender({
292
293
double ? height,
293
294
Widget Function (String ? )? altWidget,
294
295
Widget Function ()? loadingWidget,
295
- }) => CustomRender .fromWidget (widget: (context, buildChildren) {
296
+ }) => CustomRender .widget (widget: (context, buildChildren) {
296
297
final src = mapUrl? .call (_src (context.tree.element! .attributes.cast ()))
297
298
?? _src (context.tree.element! .attributes.cast ())! ;
298
- precacheImage (
299
- NetworkImage (
300
- src,
301
- headers: headers,
302
- ),
303
- context.buildContext,
304
- onError: (exception, StackTrace ? stackTrace) {
305
- context.parser.onImageError? .call (exception, stackTrace);
306
- },
307
- );
308
299
Completer <Size > completer = Completer ();
309
- Image image = Image .network (src, frameBuilder: (ctx, child, frame, _) {
310
- if (frame == null ) {
311
- if (! completer.isCompleted) {
312
- completer.completeError ("error" );
300
+ if (context.parser.cachedImageSizes[src] != null ) {
301
+ completer.complete (context.parser.cachedImageSizes[src]);
302
+ } else {
303
+ Image image = Image .network (src, frameBuilder: (ctx, child, frame, _) {
304
+ if (frame == null ) {
305
+ if (! completer.isCompleted) {
306
+ completer.completeError ("error" );
307
+ }
308
+ return child;
309
+ } else {
310
+ return child;
313
311
}
314
- return child;
315
- } else {
316
- return child;
317
- }
318
- });
312
+ });
319
313
320
- image.image. resolve ( ImageConfiguration ()). addListener (
321
- ImageStreamListener ((ImageInfo image , bool synchronousCall) {
322
- var myImage = image .image;
314
+ ImageStreamListener ? listener;
315
+ listener = ImageStreamListener ((ImageInfo imageInfo , bool synchronousCall) {
316
+ var myImage = imageInfo .image;
323
317
Size size = Size (myImage.width.toDouble (), myImage.height.toDouble ());
324
318
if (! completer.isCompleted) {
319
+ context.parser.cachedImageSizes[src] = size;
325
320
completer.complete (size);
321
+ image.image.resolve (ImageConfiguration ()).removeListener (listener! );
326
322
}
327
323
}, onError: (object, stacktrace) {
328
324
if (! completer.isCompleted) {
329
325
completer.completeError (object);
326
+ image.image.resolve (ImageConfiguration ()).removeListener (listener! );
330
327
}
331
- }),
332
- );
328
+ });
329
+
330
+ image.image.resolve (ImageConfiguration ()).addListener (listener);
331
+ }
332
+ final attributes = context.tree.element! .attributes.cast <String , String >();
333
333
final widget = FutureBuilder <Size >(
334
334
future: completer.future,
335
+ initialData: context.parser.cachedImageSizes[src],
335
336
builder: (BuildContext buildContext, AsyncSnapshot <Size > snapshot) {
336
337
if (snapshot.hasData) {
337
- return Image .network (
338
- src,
339
- headers: headers,
340
- width: width ?? _width (context.tree.element! .attributes.cast ())
341
- ?? snapshot.data! .width,
342
- height: height ?? _height (context.tree.element! .attributes.cast ()),
343
- frameBuilder: (ctx, child, frame, _) {
344
- if (frame == null ) {
345
- return altWidget? .call (_alt (context.tree.element! .attributes.cast ())) ??
346
- Text (_alt (context.tree.element! .attributes.cast ())
347
- ?? "" , style: context.style.generateTextStyle ());
348
- }
349
- return child;
350
- },
338
+ return Container (
339
+ constraints: BoxConstraints (
340
+ maxWidth: width ?? _width (attributes) ?? snapshot.data! .width,
341
+ maxHeight:
342
+ (width ?? _width (attributes) ?? snapshot.data! .width) /
343
+ _aspectRatio (attributes, snapshot)),
344
+ child: AspectRatio (
345
+ aspectRatio: _aspectRatio (attributes, snapshot),
346
+ child: Image .network (
347
+ src,
348
+ headers: headers,
349
+ width: width ?? _width (attributes) ?? snapshot.data! .width,
350
+ height: height ?? _height (attributes),
351
+ frameBuilder: (ctx, child, frame, _) {
352
+ if (frame == null ) {
353
+ return altWidget? .call (_alt (attributes)) ??
354
+ Text (_alt (attributes) ?? "" ,
355
+ style: context.style.generateTextStyle ());
356
+ }
357
+ return child;
358
+ },
359
+ ),
360
+ ),
351
361
);
352
362
} else if (snapshot.hasError) {
353
363
return altWidget? .call (_alt (context.tree.element! .attributes.cast ())) ??
@@ -359,9 +369,9 @@ CustomRender networkImageRender({
359
369
},
360
370
);
361
371
return Builder (
372
+ key: context.key,
362
373
builder: (buildContext) {
363
374
return GestureDetector (
364
- key: context.key,
365
375
child: widget,
366
376
onTap: () {
367
377
if (MultipleTapGestureDetector .of (buildContext) != null ) {
@@ -520,3 +530,21 @@ double? _width(Map<String, String> attributes) {
520
530
final widthString = attributes["width" ];
521
531
return widthString == null ? widthString as double ? : double .tryParse (widthString);
522
532
}
533
+
534
+ double _aspectRatio (
535
+ Map <String , String > attributes, AsyncSnapshot <Size > calculated) {
536
+ final heightString = attributes["height" ];
537
+ final widthString = attributes["width" ];
538
+ if (heightString != null && widthString != null ) {
539
+ final height = double .tryParse (heightString);
540
+ final width = double .tryParse (widthString);
541
+ return height == null || width == null
542
+ ? calculated.data! .aspectRatio
543
+ : width / height;
544
+ }
545
+ return calculated.data! .aspectRatio;
546
+ }
547
+
548
+ extension ClampedEdgeInsets on EdgeInsetsGeometry {
549
+ EdgeInsetsGeometry get nonNegative => this .clamp (EdgeInsets .zero, const EdgeInsets .all (double .infinity));
550
+ }
0 commit comments