diff --git a/CHANGELOG.md b/CHANGELOG.md index fcaeddd16c..9195d6ea65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +## [2.0.0] - April 29, 2021: +* Stable release with all 2.0.0-nullsafety.X changes + +## [2.0.0-nullsafety.1] - April 29, 2021: +* Support basic MathML +* Support inner links +* Supply full context tree to custom render +* Include or exclude specific tags via `tagsList` parameter +* Fixed lists not rendering correctly +* Fixes for colspans in tables +* Fixed various exceptions when using inline styles +* Fixed text decoration not cascading between parent and child +* [BREAKING] support whitelisting tags + * See the README for details on how to migrate `blacklistedElements` (deprecated) to `tagsList` +* Fixed `failed assertion` error when tap-scrolling on any link +* Updated dependencies + ## [2.0.0-nullsafety.0] - March 5, 2021: * Nullsafety support * Official Flutter Web support diff --git a/README.md b/README.md index 624a95cb70..d464ba287b 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ A Flutter widget for rendering HTML and CSS as Flutter widgets. Add the following to your `pubspec.yaml` file: dependencies: - flutter_html: ^1.3.0 + flutter_html: ^2.0.0 ## Currently Supported HTML Tags: | | | | | | | | | | | | @@ -242,17 +242,18 @@ Widget html = Html( ); ``` +Inner links (such as `Back to the top` will work out of the box by scrolling the viewport, as long as your `Html` widget is wrapped in a scroll container such as a `SingleChildScrollView`. + ### customRender: -A powerful API that allows you to customize everything when rendering a specific HTML tag. This means you can add support for HTML elements that aren't supported natively. You can also make up your own custom tags in your HTML! +A powerful API that allows you to customize everything when rendering a specific HTML tag. This means you can change the default behaviour or add support for HTML elements that aren't supported natively. You can also make up your own custom tags in your HTML! -`customRender` accepts a `Map`. The `CustomRender` type is a function that requires a `Widget` to be returned. It exposes `RenderContext`, the `Widget` that would have been rendered by `Html` without a `customRender` defined, the `attributes` of the HTML element as a `Map`, and the HTML element itself as `Element`. +`customRender` accepts a `Map`. The `CustomRender` type is a function that requires a `Widget` or `InlineSpan` to be returned. It exposes `RenderContext` and the `Widget` that would have been rendered by `Html` without a `customRender` defined. The `RenderContext` contains the build context, styling and the HTML element, with attrributes and its subtree,. -To use this API, set the key as the tag of the HTML element you wish to provide a custom implementation for, and create a function with the above parameters that returns a `Widget`. +To use this API, set the key as the tag of the HTML element you wish to provide a custom implementation for, and create a function with the above parameters that returns a `Widget` or `InlineSpan`. #### Example Usages - customRender: 1. Simple example - rendering custom HTML tags -
View code ```dart Widget html = Html( @@ -262,24 +263,48 @@ Widget html = Html( """, customRender: { - "bird": (RenderContext context, Widget child, Map attributes, dom.Element? element) { + "bird": (RenderContext context, Widget child) { return TextSpan(text: "🐦"); }, - "flutter": (RenderContext context, Widget child, Map attributes, dom.Element? element) { + "flutter": (RenderContext context, Widget child) { return FlutterLogo( - style: (attributes['horizontal'] != null) + style: (context.tree.element!.attributes['horizontal'] != null) ? FlutterLogoStyle.horizontal : FlutterLogoStyle.markOnly, textColor: context.style.color, - size: context.style.fontSize.size * 5, + size: context.style.fontSize!.size! * 5, ); }, }, ); ``` -
-2. Complex example - rendering an `iframe` differently based on whether it is an embedded youtube video or some other embedded content +2. Complex example - wrapping the default widget with your own, in this case placing a horizontal scroll around a (potentially too wide) table. + +
View code + +```dart +Widget html = Html( + data: """ + + + + + +
Monthly savings
January February March April May June July August September October November December
\$100 \$50 \$80 \$60 \$90 \$140 \$110 \$80 \$90 \$60 \$40 \$70
\90 \$60 \$80 \$80 \$100 \$160 \$150 \$110 \$100 \$60 \$30 \$80
+ """, + customRender: { + "table": (context, child) { + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: (context.tree as TableLayoutElement).toWidget(context), + ); + } + }, +); +``` + +3. Complex example - rendering an `iframe` differently based on whether it is an embedded youtube video or some other embedded content.
View code @@ -292,25 +317,26 @@ Widget html = Html( """, customRender: { - "iframe": (RenderContext context, Widget child, Map attributes, dom.Element? element) { - if (attributes != null) { - double width = double.tryParse(attributes['width'] ?? ""); - double height = double.tryParse(attributes['height'] ?? ""); + "iframe": (RenderContext context, Widget child) { + final attrs = context.tree.element?.attributes; + if (attrs != null) { + double? width = double.tryParse(attrs['width'] ?? ""); + double? height = double.tryParse(attrs['height'] ?? ""); return Container( width: width ?? (height ?? 150) * 2, height: height ?? (width ?? 300) / 2, child: WebView( - initialUrl: attributes['src'] ?? "about:blank", + initialUrl: attrs['src'] ?? "about:blank", javascriptMode: JavascriptMode.unrestricted, //no need for scrolling gesture recognizers on embedded youtube, so set gestureRecognizers null //on other iframe content scrolling might be necessary, so use VerticalDragGestureRecognizer - gestureRecognizers: attributes['src'] != null && attributes['src']!.contains("youtube.com/embed") ? null : [ + gestureRecognizers: attrs['src'] != null && attrs['src']!.contains("youtube.com/embed") ? null : [ Factory(() => VerticalDragGestureRecognizer()) ].toSet(), navigationDelegate: (NavigationRequest request) async { //no need to load any url besides the embedded youtube url when displaying embedded youtube, so prevent url loading //on other iframe content allow all url loading - if (attributes['src'] != null && attributes['src']!.contains("youtube.com/embed")) { + if (attrs['src'] != null && attrs['src']!.contains("youtube.com/embed")) { if (!request.url.contains("youtube.com/embed")) { return NavigationDecision.prevent; } else { diff --git a/example/lib/main.dart b/example/lib/main.dart index 665fc768f7..ec1275b915 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -268,7 +268,19 @@ class _MyHomePageState extends State { scrollDirection: Axis.horizontal, child: (context.tree as TableLayoutElement).toWidget(context), ); - } + }, + "bird": (RenderContext context, Widget child) { + return TextSpan(text: "🐦"); + }, + "flutter": (RenderContext context, Widget child) { + return FlutterLogo( + style: (context.tree.element!.attributes['horizontal'] != null) + ? FlutterLogoStyle.horizontal + : FlutterLogoStyle.markOnly, + textColor: context.style.color!, + size: context.style.fontSize!.size! * 5, + ); + }, }, customImageRenders: { networkSourceMatcher(domains: ["flutter.dev"]): (context, attributes, element) { diff --git a/pubspec.yaml b/pubspec.yaml index 65c066f0fa..04b7d5a03b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_html description: A Flutter widget rendering static HTML and CSS as Flutter widgets. -version: 2.0.0-nullsafety.0 +version: 2.0.0-nullsafety.1 homepage: https://github.com/Sub6Resources/flutter_html environment: @@ -44,4 +44,3 @@ dev_dependencies: sdk: flutter flutter: -