Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support relative paths for network image rendering #532

Merged
merged 3 commits into from
Feb 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ The default image renders are:
```dart
final Map<ImageSourceMatcher, ImageRender> defaultImageRenders = {
base64UriMatcher(): base64ImageRender(),
assetUriMatcher(): assetImageRender(),
networkSourceMatcher(extension: "svg"): svgNetworkImageRender(),
networkSourceMatcher(): networkImageRender(),
};
Expand Down Expand Up @@ -535,12 +536,16 @@ Widget html = Html(
headers: {"Custom-Header": "some-value"},
altWidget: (alt) => Text(alt),
),
(attr, _) => attr["src"] != null && attr["src"].startsWith("/wiki"):
networkImageRender(
mapUrl: (url) => "https://upload.wikimedia.org" + url),
},
);
```

Above, there are two `networkSourceMatcher`s. One is overriden to only apply to images on the URL `flutter.dev`, while the other covers the rest of the cases.
When an image with URL `flutter.dev` is detected, rather than displaying the image, the render will display the flutter logo. If the image is any other image, it keeps the default widget, but just sets the headers and the alt text in case that image happens to be broken.
Above, there are three custom `networkSourceMatcher`s, which will be applied - in order - before the default implementations.

When an image with URL `flutter.dev` is detected, rather than displaying the image, the render will display the flutter logo. If the image is any other image, it keeps the default widget, but just sets the headers and the alt text in case that image happens to be broken. The final render handles relative paths by rewriting them, specifically prefixing them with a base url. Note that the customizations of the previous custom renders do not apply. For example, the headers that the second render would apply are not applied in this third render.

2. Creating your own renders:
```dart
Expand Down
26 changes: 8 additions & 18 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -127,31 +127,15 @@ const htmlData = """
<img src='asset:assets/mac.svg' width='100' />
<h3>Base64</h3>
<img alt='Red dot' src='data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==' />
<h3>Custom source matcher (relative paths)</h3>
<img src='/wikipedia/commons/thumb/e/ef/Octicons-logo-github.svg/200px-Octicons-logo-github.svg.png' />
<h3>Custom image render (flutter.dev)</h3>
<img src='https://flutter.dev/images/flutter-mono-81x100.png' />
<h3>No image source</h3>
<img alt='No source' />
<img alt='Empty source' src='' />
<h3>Broken network image</h3>
<img alt='Broken image' src='https://www.notgoogle.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png' />
<h3>Used inside a table</h3>
<table>
<tr>
<td><img alt='Google' src='https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png' /></td>
<td><img alt='Google' src='https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png' /></td>
<td><img alt='Google' src='https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png' /></td>
</tr>
</table>
<h3>Video support:</h3>
<video controls>
<source src="https://www.w3schools.com/html/mov_bbb.mp4" />
</video>
<h3>Audio support:</h3>
<audio controls>
<source src="https://www.w3schools.com/html/mov_bbb.mp4" />
</audio>
<h3>IFrame support:</h3>
<iframe src="https://google.com"></iframe>
""";

class _MyHomePageState extends State<MyHomePage> {
Expand All @@ -175,6 +159,12 @@ class _MyHomePageState extends State<MyHomePage> {
headers: {"Custom-Header": "some-value"},
altWidget: (alt) => Text(alt),
),
// On relative paths starting with /wiki, prefix with a base url
(attr, _) => attr["src"] != null && attr["src"].startsWith("/wiki"):
networkImageRender(
mapUrl: (url) => "https://upload.wikimedia.org" + url),
// Custom placeholder image for broken links
networkSourceMatcher(): networkImageRender(altWidget: (_) => FlutterLogo()),
},
onLinkTap: (url) {
print("Opening $url...");
Expand Down
10 changes: 6 additions & 4 deletions lib/image_render.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,16 @@ ImageRender assetImageRender({

ImageRender networkImageRender({
Map<String, String> headers,
String Function(String) mapUrl,
double width,
double height,
Widget Function(String) altWidget,
}) =>
(context, attributes, element) {
final src = mapUrl?.call(_src(attributes)) ?? _src(attributes);
precacheImage(
NetworkImage(
_src(attributes),
src,
headers: headers,
),
context.buildContext,
Expand All @@ -107,7 +109,7 @@ ImageRender networkImageRender({
);
Completer<Size> completer = Completer();
Image image =
Image.network(_src(attributes), frameBuilder: (ctx, child, frame, _) {
Image.network(src, frameBuilder: (ctx, child, frame, _) {
if (frame == null) {
if (!completer.isCompleted) {
completer.completeError("error");
Expand Down Expand Up @@ -137,7 +139,7 @@ ImageRender networkImageRender({
builder: (BuildContext buildContext, AsyncSnapshot<Size> snapshot) {
if (snapshot.hasData) {
return Image.network(
_src(attributes),
src,
headers: headers,
width: width ?? _width(attributes) ?? snapshot.data.width,
height: height ?? _height(attributes),
Expand All @@ -151,7 +153,7 @@ ImageRender networkImageRender({
},
);
} else if (snapshot.hasError) {
return Text(_alt(attributes) ?? "",
return altWidget?.call(_alt(attributes)) ?? Text(_alt(attributes) ?? "",
style: context.style.generateTextStyle());
} else {
return new CircularProgressIndicator();
Expand Down