Skip to content

Commit e421c47

Browse files
authored
Merge pull request Sub6Resources#548 from tneotia/misc/nullsafety
Nullsafety
2 parents 328ccff + 6cbbdba commit e421c47

19 files changed

+618
-636
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
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+
114
## [1.3.0] - February 16, 2021:
215
* New image loading API
316
* Image loading with request headers, from relative paths and custom loading widget

README.md

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ Widget html = Html(
171171
data: """<p>
172172
Linking to <a href='https://github.com'>websites</a> has never been easier.
173173
</p>""",
174-
onLinkTap: (String url) {
174+
onLinkTap: (String? url, RenderContext context, Map<String, String> attributes, dom.Element? element) {
175175
//open URL in webview, or launch URL in browser, or any other logic here
176176
}
177177
);
@@ -197,10 +197,10 @@ Widget html = Html(
197197
<flutter horizontal></flutter>
198198
""",
199199
customRender: {
200-
"bird": (RenderContext context, Widget child, Map<String, String> attributes, _) {
200+
"bird": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element) {
201201
return TextSpan(text: "🐦");
202202
},
203-
"flutter": (RenderContext context, Widget child, Map<String, String> attributes, _) {
203+
"flutter": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element) {
204204
return FlutterLogo(
205205
style: (attributes['horizontal'] != null)
206206
? FlutterLogoStyle.horizontal
@@ -227,26 +227,25 @@ Widget html = Html(
227227
<iframe src="https://www.youtube.com/embed/tgbNymZ7vqY"></iframe>
228228
""",
229229
customRender: {
230-
"iframe": (RenderContext context, Widget child, Map<String, String> attributes, _) {
230+
"iframe": (RenderContext context, Widget child, Map<String, String> attributes, dom.Element? element) {
231231
if (attributes != null) {
232232
double width = double.tryParse(attributes['width'] ?? "");
233233
double height = double.tryParse(attributes['height'] ?? "");
234-
print(attributes['src']);
235234
return Container(
236235
width: width ?? (height ?? 150) * 2,
237236
height: height ?? (width ?? 300) / 2,
238237
child: WebView(
239-
initialUrl: attributes['src'],
238+
initialUrl: attributes['src'] ?? "about:blank",
240239
javascriptMode: JavascriptMode.unrestricted,
241240
//no need for scrolling gesture recognizers on embedded youtube, so set gestureRecognizers null
242241
//on other iframe content scrolling might be necessary, so use VerticalDragGestureRecognizer
243-
gestureRecognizers: attributes['src'].contains("youtube.com/embed") ? null : [
242+
gestureRecognizers: attributes['src'] != null && attributes['src']!.contains("youtube.com/embed") ? null : [
244243
Factory(() => VerticalDragGestureRecognizer())
245244
].toSet(),
246245
navigationDelegate: (NavigationRequest request) async {
247246
//no need to load any url besides the embedded youtube url when displaying embedded youtube, so prevent url loading
248247
//on other iframe content allow all url loading
249-
if (attributes['src'].contains("youtube.com/embed")) {
248+
if (attributes['src'] != null && attributes['src']!.contains("youtube.com/embed")) {
250249
if (!request.url.contains("youtube.com/embed")) {
251250
return NavigationDecision.prevent;
252251
} else {
@@ -293,7 +292,7 @@ A function that defines what the widget should do when an image is tapped.
293292
```dart
294293
Widget html = Html(
295294
data: """<img alt='Google' src='https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png' />""",
296-
onImageTap: (String url) {
295+
onImageTap: (String? url, RenderContext context, Map<String, String> attributes, dom.Element? element) {
297296
//open image in webview, or launch image in browser, or any other logic here
298297
}
299298
);
@@ -432,8 +431,9 @@ A typical usage would look something like this:
432431

433432
```dart
434433
ImageSourceMatcher base64UriMatcher() => (attributes, element) =>
435-
attributes["src"].startsWith("data:image") &&
436-
attributes["src"].contains("base64,");
434+
attributes["src"] != null &&
435+
attributes["src"]!.startsWith("data:image") &&
436+
attributes["src"]!.contains("base64,");
437437
```
438438

439439
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.
@@ -448,10 +448,10 @@ ImageSourceMatcher networkSourceMatcher({
448448
String extension: "your extension",
449449
}) =>
450450
(attributes, element) {
451-
final src = Uri.parse(attributes["src"]);
451+
final src = Uri.parse(attributes["src"] ?? "about:blank");
452452
return schemas.contains(src.scheme) &&
453-
(domains == null || domains.contains(src.host)) &&
454-
(extension == null || src.path.endsWith(".$extension"));
453+
domains.contains(src.host) &&
454+
src.path.endsWith(".$extension");
455455
};
456456
```
457457

@@ -465,7 +465,8 @@ A typical usage might look like this:
465465

466466
```dart
467467
ImageRender base64ImageRender() => (context, attributes, element) {
468-
final decodedImage = base64.decode(attributes["src"].split("base64,")[1].trim());
468+
final decodedImage = base64.decode(attributes["src"] != null ?
469+
attributes["src"].split("base64,")[1].trim() : "about:blank");
469470
return Image.memory(
470471
decodedImage,
471472
);
@@ -485,7 +486,7 @@ ImageRender networkImageRender({
485486
}) =>
486487
(context, attributes, element) {
487488
return Image.network(
488-
attributes["src"],
489+
attributes["src"] ?? "about:blank",
489490
headers: headers,
490491
width: width,
491492
height: height,
@@ -521,10 +522,10 @@ Widget html = Html(
521522
},
522523
networkSourceMatcher(): networkImageRender(
523524
headers: {"Custom-Header": "some-value"},
524-
altWidget: (alt) => Text(alt),
525+
altWidget: (alt) => Text(alt ?? ""),
525526
loadingWidget: () => Text("Loading..."),
526527
),
527-
(attr, _) => attr["src"] != null && attr["src"].startsWith("/wiki"):
528+
(attr, _) => attr["src"] != null && attr["src"]!.startsWith("/wiki"):
528529
networkImageRender(
529530
mapUrl: (url) => "https://upload.wikimedia.org" + url),
530531
},
@@ -538,16 +539,17 @@ When an image with URL `flutter.dev` is detected, rather than displaying the ima
538539
2. Creating your own renders:
539540
```dart
540541
ImageSourceMatcher classAndIdMatcher({String classToMatch, String idToMatch}) => (attributes, element) =>
541-
attributes["class"].contains(classToMatch) ||
542-
attributes["id"].contains(idToMatch);
542+
attributes["class"] != null && attributes["id"] != null &&
543+
(attributes["class"]!.contains(classToMatch) ||
544+
attributes["id"]!.contains(idToMatch));
543545
544546
ImageRender classAndIdRender({String classToMatch, String idToMatch}) => (context, attributes, element) {
545-
if (attributes["class"].contains(classToMatch)) {
546-
return Image.asset(attributes["src"]);
547+
if (attributes["class"] != null && attributes["class"]!.contains(classToMatch)) {
548+
return Image.asset(attributes["src"] ?? "about:blank");
547549
} else {
548550
return Image.network(
549-
attributes["src"],
550-
semanticLabel: attributes["longdesc"],
551+
attributes["src"] ?? "about:blank",
552+
semanticLabel: attributes["longdesc"] ?? "",
551553
width: attributes["width"],
552554
height: attributes["height"],
553555
color: context.style.color,
@@ -609,9 +611,7 @@ You can set the `navigationDelegate` of the webview with the `navigationDelegate
609611

610612
### Audio
611613

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

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

example/lib/main.dart

Lines changed: 4 additions & 4 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,13 +157,13 @@ 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
},

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:

lib/flutter_html.dart

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,27 +33,27 @@ class Html extends StatelessWidget {
3333
/// **style** Pass in the style information for the Html here.
3434
/// See [its wiki page](https://github.com/Sub6Resources/flutter_html/wiki/Style) for more info.
3535
Html({
36-
Key key,
37-
@required this.data,
36+
Key? key,
37+
required this.data,
3838
this.onLinkTap,
39-
this.customRender,
39+
this.customRender = const {},
4040
this.customImageRenders = const {},
4141
this.onImageError,
4242
this.shrinkWrap = false,
4343
this.onImageTap,
4444
this.blacklistedElements = const [],
45-
this.style,
45+
this.style = const {},
4646
this.navigationDelegateForIframe,
4747
}) : super(key: key);
4848

4949
final String data;
50-
final OnTap onLinkTap;
50+
final OnTap? onLinkTap;
5151
final Map<ImageSourceMatcher, ImageRender> customImageRenders;
52-
final ImageErrorListener onImageError;
52+
final ImageErrorListener? onImageError;
5353
final bool shrinkWrap;
5454

5555
/// Properties for the Image widget that gets rendered by the rich text parser
56-
final OnTap onImageTap;
56+
final OnTap? onImageTap;
5757

5858
final List<String> blacklistedElements;
5959

@@ -67,11 +67,11 @@ class Html extends StatelessWidget {
6767
/// Decides how to handle a specific navigation request in the WebView of an
6868
/// Iframe. It's necessary to use the webview_flutter package inside the app
6969
/// to use NavigationDelegate.
70-
final NavigationDelegate navigationDelegateForIframe;
70+
final NavigationDelegate? navigationDelegateForIframe;
7171

7272
@override
7373
Widget build(BuildContext context) {
74-
final double width = shrinkWrap ? null : MediaQuery.of(context).size.width;
74+
final double? width = shrinkWrap ? null : MediaQuery.of(context).size.width;
7575

7676
return Container(
7777
width: width,

0 commit comments

Comments
 (0)