Skip to content

Commit acf383b

Browse files
committed
Merge branch 'master' of https://github.com/Sub6Resources/flutter_html into feature/more-inline-styles
� Conflicts: � lib/src/css_parser.dart � lib/src/utils.dart
2 parents 64a0078 + e421c47 commit acf383b

26 files changed

+880
-719
lines changed

CHANGELOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
## [2.0.0-nullsafety.0] - March 5, 2021:
2+
* Nullsafety support
3+
* Official Flutter Web support
4+
* New features & fixes for lists:
5+
* Support start attribute (e.g. `start="5";`)
6+
* Support RTL direction
7+
* Support setting padding - you can remove the starting padding if you choose
8+
* Fixed unknown character box on iOS when font-weight is below w400
9+
* Upgraded link functions to provide more granular control
10+
* Fixed errors in text-decoration parsing
11+
* Fixed `<audio>` on iOS ("_duration called on null" exception)
12+
* Updated dependencies
13+
14+
## [1.3.0] - February 16, 2021:
15+
* New image loading API
16+
* Image loading with request headers, from relative paths and custom loading widget
17+
* SVG image support from network or local assets
18+
* Support for `<details>`/`<summary>` tags
19+
* Allow returning spans from custom tag renders
20+
* Inline font styling
21+
* Content-based table column sizing
22+
* Respect iframe sandbox attribute
23+
* Fixed text flow and styling when using tags inside `<a>` links
24+
* Fixed issue where `shrinkWrap` property would not constrain the widget to take up the space it needs
25+
* See the [Notes](https://github.com/Sub6Resources/flutter_html#notes) for an example usage with `shrinkWrap`
26+
* Fixed issue where iframes would not update when their `src`s changed in the HTML data
27+
* Updated dependencies for Flutter 1.26+
28+
129
## [1.2.0] - January 14, 2021:
230
* Support irregular table sizes
331
* Allow for returning `null` from a customRender function to disable the widget

README.md

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -37,43 +37,27 @@ A Flutter widget for rendering HTML and CSS as Flutter widgets.
3737

3838
- [Data](#data)
3939

40-
- [Example](#example-usage---data)
41-
4240
- [onLinkTap](#onlinktap)
4341

44-
- [Example](#example-usage---onlinktap)
45-
4642
- [customRender](#customrender)
4743

48-
- [Example](#example-usages---customrender)
49-
5044
- [onImageError](#onimageerror)
5145

52-
- [Example](#example-usage---onimageerror)
53-
5446
- [onImageTap](#onimagetap)
5547

56-
- [Example](#example-usage---onimagetap)
57-
5848
- [blacklistedElements](#blacklistedelements)
5949

60-
- [Example](#example-usage---blacklistedelements)
61-
6250
- [style](#style)
6351

64-
- [Example](#example-usage---style)
65-
6652
- [navigationDelegateForIframe](#navigationdelegateforiframe)
6753

68-
- [Example](#example-usage---navigationdelegateforiframe)
69-
7054
- [customImageRender](#customimagerender)
7155

7256
- [typedef ImageSourceMatcher (with examples)](#typedef-imagesourcematcher)
7357

7458
- [typedef ImageRender (with examples)](#typedef-imagerender)
7559

76-
- [Examples](#example-usages---customimagerender)
60+
- [Extended examples](#example-usages---customimagerender)
7761

7862
- [Rendering Reference](#rendering-reference)
7963

@@ -100,7 +84,7 @@ A Flutter widget for rendering HTML and CSS as Flutter widgets.
10084
Add the following to your `pubspec.yaml` file:
10185

10286
dependencies:
103-
flutter_html: ^1.2.0
87+
flutter_html: ^1.3.0
10488

10589
## Currently Supported HTML Tags:
10690
| | | | | | | | | | | |
@@ -196,7 +180,7 @@ Widget html = Html(
196180
data: """<p>
197181
Linking to <a href='https://github.com'>websites</a> has never been easier.
198182
</p>""",
199-
onLinkTap: (String url) {
183+
onLinkTap: (String? url, RenderContext context, Map<String, String> attributes, dom.Element? element) {
200184
//open URL in webview, or launch URL in browser, or any other logic here
201185
}
202186
);
@@ -222,10 +206,10 @@ Widget html = Html(
222206
<flutter horizontal></flutter>
223207
""",
224208
customRender: {
225-
"bird": (RenderContext context, Widget child, Map<String, String> attributes, _) {
209+
"bird": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element) {
226210
return TextSpan(text: "🐦");
227211
},
228-
"flutter": (RenderContext context, Widget child, Map<String, String> attributes, _) {
212+
"flutter": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element) {
229213
return FlutterLogo(
230214
style: (attributes['horizontal'] != null)
231215
? FlutterLogoStyle.horizontal
@@ -252,26 +236,25 @@ Widget html = Html(
252236
<iframe src="https://www.youtube.com/embed/tgbNymZ7vqY"></iframe>
253237
""",
254238
customRender: {
255-
"iframe": (RenderContext context, Widget child, Map<String, String> attributes, _) {
239+
"iframe": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element) {
256240
if (attributes != null) {
257241
double width = double.tryParse(attributes['width'] ?? "");
258242
double height = double.tryParse(attributes['height'] ?? "");
259-
print(attributes['src']);
260243
return Container(
261244
width: width ?? (height ?? 150) * 2,
262245
height: height ?? (width ?? 300) / 2,
263246
child: WebView(
264-
initialUrl: attributes['src'],
247+
initialUrl: attributes['src'] ?? "about:blank",
265248
javascriptMode: JavascriptMode.unrestricted,
266249
//no need for scrolling gesture recognizers on embedded youtube, so set gestureRecognizers null
267250
//on other iframe content scrolling might be necessary, so use VerticalDragGestureRecognizer
268-
gestureRecognizers: attributes['src'].contains("youtube.com/embed") ? null : [
251+
gestureRecognizers: attributes['src'] != null && attributes['src']!.contains("youtube.com/embed") ? null : [
269252
Factory(() => VerticalDragGestureRecognizer())
270253
].toSet(),
271254
navigationDelegate: (NavigationRequest request) async {
272255
//no need to load any url besides the embedded youtube url when displaying embedded youtube, so prevent url loading
273256
//on other iframe content allow all url loading
274-
if (attributes['src'].contains("youtube.com/embed")) {
257+
if (attributes['src'] != null && attributes['src']!.contains("youtube.com/embed")) {
275258
if (!request.url.contains("youtube.com/embed")) {
276259
return NavigationDecision.prevent;
277260
} else {
@@ -318,7 +301,7 @@ A function that defines what the widget should do when an image is tapped.
318301
```dart
319302
Widget html = Html(
320303
data: """<img alt='Google' src='https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png' />""",
321-
onImageTap: (String url) {
304+
onImageTap: (String? url, RenderContext context, Map<String, String> attributes, dom.Element? element) {
322305
//open image in webview, or launch image in browser, or any other logic here
323306
}
324307
);
@@ -457,8 +440,9 @@ A typical usage would look something like this:
457440

458441
```dart
459442
ImageSourceMatcher base64UriMatcher() => (attributes, element) =>
460-
attributes["src"].startsWith("data:image") &&
461-
attributes["src"].contains("base64,");
443+
attributes["src"] != null &&
444+
attributes["src"]!.startsWith("data:image") &&
445+
attributes["src"]!.contains("base64,");
462446
```
463447

464448
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.
@@ -473,10 +457,10 @@ ImageSourceMatcher networkSourceMatcher({
473457
String extension: "your extension",
474458
}) =>
475459
(attributes, element) {
476-
final src = Uri.parse(attributes["src"]);
460+
final src = Uri.parse(attributes["src"] ?? "about:blank");
477461
return schemas.contains(src.scheme) &&
478-
(domains == null || domains.contains(src.host)) &&
479-
(extension == null || src.path.endsWith(".$extension"));
462+
domains.contains(src.host) &&
463+
src.path.endsWith(".$extension");
480464
};
481465
```
482466

@@ -490,7 +474,8 @@ A typical usage might look like this:
490474

491475
```dart
492476
ImageRender base64ImageRender() => (context, attributes, element) {
493-
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");
494479
return Image.memory(
495480
decodedImage,
496481
);
@@ -510,7 +495,7 @@ ImageRender networkImageRender({
510495
}) =>
511496
(context, attributes, element) {
512497
return Image.network(
513-
attributes["src"],
498+
attributes["src"] ?? "about:blank",
514499
headers: headers,
515500
width: width,
516501
height: height,
@@ -546,10 +531,10 @@ Widget html = Html(
546531
},
547532
networkSourceMatcher(): networkImageRender(
548533
headers: {"Custom-Header": "some-value"},
549-
altWidget: (alt) => Text(alt),
534+
altWidget: (alt) => Text(alt ?? ""),
550535
loadingWidget: () => Text("Loading..."),
551536
),
552-
(attr, _) => attr["src"] != null && attr["src"].startsWith("/wiki"):
537+
(attr, _) => attr["src"] != null && attr["src"]!.startsWith("/wiki"):
553538
networkImageRender(
554539
mapUrl: (url) => "https://upload.wikimedia.org" + url),
555540
},
@@ -563,16 +548,17 @@ When an image with URL `flutter.dev` is detected, rather than displaying the ima
563548
2. Creating your own renders:
564549
```dart
565550
ImageSourceMatcher classAndIdMatcher({String classToMatch, String idToMatch}) => (attributes, element) =>
566-
attributes["class"].contains(classToMatch) ||
567-
attributes["id"].contains(idToMatch);
551+
attributes["class"] != null && attributes["id"] != null &&
552+
(attributes["class"]!.contains(classToMatch) ||
553+
attributes["id"]!.contains(idToMatch));
568554
569555
ImageRender classAndIdRender({String classToMatch, String idToMatch}) => (context, attributes, element) {
570-
if (attributes["class"].contains(classToMatch)) {
571-
return Image.asset(attributes["src"]);
556+
if (attributes["class"] != null && attributes["class"]!.contains(classToMatch)) {
557+
return Image.asset(attributes["src"] ?? "about:blank");
572558
} else {
573559
return Image.network(
574-
attributes["src"],
575-
semanticLabel: attributes["longdesc"],
560+
attributes["src"] ?? "about:blank",
561+
semanticLabel: attributes["longdesc"] ?? "",
576562
width: attributes["width"],
577563
height: attributes["height"],
578564
color: context.style.color,
@@ -634,9 +620,7 @@ You can set the `navigationDelegate` of the webview with the `navigationDelegate
634620

635621
### Audio
636622

637-
This package renders audio elements using the [`chewie_audio`](https://pub.dev/packages/chewie_audio) plugin.
638-
639-
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.
640624

641625
The package considers the attributes `controls`, `loop`, `src`, `autoplay`, `width`, and `muted` when rendering the audio widget.
642626

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//
2+
// Generated file. Do not edit.
3+
//
4+
5+
// ignore_for_file: lines_longer_than_80_chars
6+
7+
import 'package:video_player_web/video_player_web.dart';
8+
import 'package:wakelock_web/wakelock_web.dart';
9+
10+
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
11+
12+
// ignore: public_member_api_docs
13+
void registerPlugins(Registrar registrar) {
14+
VideoPlayerPlugin.registerWith(registrar);
15+
WakelockWeb.registerWith(registrar);
16+
registrar.registerMessageHandler();
17+
}

example/lib/main.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class MyApp extends StatelessWidget {
1919
}
2020

2121
class MyHomePage extends StatefulWidget {
22-
MyHomePage({Key key, this.title}) : super(key: key);
22+
MyHomePage({Key? key, required this.title}) : super(key: key);
2323

2424
final String title;
2525

@@ -157,20 +157,20 @@ class _MyHomePageState extends State<MyHomePage> {
157157
},
158158
networkSourceMatcher(domains: ["mydomain.com"]): networkImageRender(
159159
headers: {"Custom-Header": "some-value"},
160-
altWidget: (alt) => Text(alt),
160+
altWidget: (alt) => Text(alt ?? ""),
161161
loadingWidget: () => Text("Loading..."),
162162
),
163163
// On relative paths starting with /wiki, prefix with a base url
164-
(attr, _) => attr["src"] != null && attr["src"].startsWith("/wiki"):
164+
(attr, _) => attr["src"] != null && attr["src"]!.startsWith("/wiki"):
165165
networkImageRender(
166-
mapUrl: (url) => "https://upload.wikimedia.org" + url),
166+
mapUrl: (url) => "https://upload.wikimedia.org" + url!),
167167
// Custom placeholder image for broken links
168168
networkSourceMatcher(): networkImageRender(altWidget: (_) => FlutterLogo()),
169169
},
170-
onLinkTap: (url) {
170+
onLinkTap: (url, _, __, ___) {
171171
print("Opening $url...");
172172
},
173-
onImageTap: (src) {
173+
onImageTap: (src, _, __, ___) {
174174
print(src);
175175
},
176176
onImageError: (exception, stackTrace) {

example/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: flutter_html example app.
44
version: 1.0.0+1
55

66
environment:
7-
sdk: ">=2.1.0 <3.0.0"
7+
sdk: '>=2.12.0 <3.0.0'
88

99
dependencies:
1010
flutter_html:

example/web/favicon.png

917 Bytes
Loading

example/web/icons/Icon-192.png

5.17 KB
Loading

example/web/icons/Icon-512.png

8.06 KB
Loading

example/web/index.html

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<!--
5+
If you are serving your web app in a path other than the root, change the
6+
href value below to reflect the base path you are serving from.
7+
8+
The path provided below has to start and end with a slash "/" in order for
9+
it to work correctly.
10+
11+
Fore more details:
12+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
13+
-->
14+
<base href="/">
15+
16+
<meta charset="UTF-8">
17+
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
18+
<meta name="description" content="A new Flutter project.">
19+
20+
<!-- iOS meta tags & icons -->
21+
<meta name="apple-mobile-web-app-capable" content="yes">
22+
<meta name="apple-mobile-web-app-status-bar-style" content="black">
23+
<meta name="apple-mobile-web-app-title" content="example">
24+
<link rel="apple-touch-icon" href="icons/Icon-192.png">
25+
26+
<!-- Favicon -->
27+
<link rel="icon" type="image/png" href="favicon.png"/>
28+
29+
<title>example</title>
30+
<link rel="manifest" href="manifest.json">
31+
</head>
32+
<body>
33+
<!-- This script installs service_worker.js to provide PWA functionality to
34+
application. For more information, see:
35+
https://developers.google.com/web/fundamentals/primers/service-workers -->
36+
<script>
37+
if ('serviceWorker' in navigator) {
38+
window.addEventListener('flutter-first-frame', function () {
39+
navigator.serviceWorker.register('flutter_service_worker.js');
40+
});
41+
}
42+
</script>
43+
<script src="main.dart.js" type="application/javascript"></script>
44+
</body>
45+
</html>

example/web/manifest.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "example",
3+
"short_name": "example",
4+
"start_url": ".",
5+
"display": "standalone",
6+
"background_color": "#0175C2",
7+
"theme_color": "#0175C2",
8+
"description": "A new Flutter project.",
9+
"orientation": "portrait-primary",
10+
"prefer_related_applications": false,
11+
"icons": [
12+
{
13+
"src": "icons/Icon-192.png",
14+
"sizes": "192x192",
15+
"type": "image/png"
16+
},
17+
{
18+
"src": "icons/Icon-512.png",
19+
"sizes": "512x512",
20+
"type": "image/png"
21+
}
22+
]
23+
}

0 commit comments

Comments
 (0)