diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 16c40c66b9..0000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,13 +0,0 @@ -version: 2.1 -orbs: - codecov: codecov/codecov@1.0.2 -jobs: - build: - docker: - - image: cirrusci/flutter - steps: - - checkout - - run: flutter --version - - run: flutter test --coverage - - codecov/upload: - file: coverage/lcov.info diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 6418d1dee6..6627ba6868 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,6 +1,6 @@ # These are supported funding model platforms -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +github: Sub6Resources patreon: # Replace with a single Patreon username open_collective: flutter_html ko_fi: # Replace with a single Ko-fi username diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..52aaf0660e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,46 @@ +--- +name: Bug report +about: Something isn't working as intended +title: "[BUG]" +labels: bug +assignees: '' + +--- + + + +**Describe the bug:** + + +**HTML to reproduce the issue:** + + +**`Html` widget configuration:** + + +**Expected behavior:** + + +**Screenshots:** + + +**Device details and Flutter/Dart/`flutter_html` versions:** + + +**Stacktrace/Logcat** + + +**Additional info:** + + +**A picture of a cute animal (not mandatory but encouraged)** + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..48d4188102 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,27 @@ +--- +name: Feature request +about: Suggest an idea for flutter_html +title: "[FEATURE]" +labels: enhancement +assignees: '' + +--- + + + +**Describe your feature request** + + +**Additional context** + + +**A picture of a cute animal (not mandatory but encouraged)** + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000000..8ecf5cff57 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,20 @@ +--- +name: Question +about: Ask a question about flutter_html +title: "[QUESTION]" +labels: question +assignees: '' + +--- + + + +Type question here. + +**A picture of a cute animal (not mandatory but encouraged)** + diff --git a/.github/flutter_html_screenshot.png b/.github/flutter_html_screenshot.png deleted file mode 100644 index 3d1b893b61..0000000000 Binary files a/.github/flutter_html_screenshot.png and /dev/null differ diff --git a/.github/flutter_html_screenshot2.png b/.github/flutter_html_screenshot2.png deleted file mode 100644 index eb47b0e146..0000000000 Binary files a/.github/flutter_html_screenshot2.png and /dev/null differ diff --git a/.github/flutter_html_screenshot3.png b/.github/flutter_html_screenshot3.png deleted file mode 100644 index 75a065879d..0000000000 Binary files a/.github/flutter_html_screenshot3.png and /dev/null differ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..3bd2e1fd11 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,33 @@ +name: flutter_html tests + +on: + pull_request: + branches: [ master ] + push: + branches: [ master ] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1 + - uses: flutter-actions/setup-flutter@54feb1e258158303e041b9eaf89314dcfbf6d38a + - name: Setup Melos + run: flutter pub global activate melos + - name: Bootstrap Project + run: melos bootstrap + - name: Run Test Suite + run: flutter pub global run melos run test + - name: Compile Test Coverage Report + run: flutter pub global run melos run gen_coverage + - name: Upload Coverage to Codecov + uses: codecov/codecov-action@v5 + with: + files: coverage_report/lcov.info + disable_search: true + token: ${{ secrets.CODECOV_TOKEN }} + - name: Run Dart Analysis + run: flutter pub global run melos analyze --fatal-infos + - name: Check that `dart format` has been run on every file + run: dart format -o none --set-exit-if-changed . diff --git a/.gitignore b/.gitignore index 34258e6aad..188d2e1fa5 100644 --- a/.gitignore +++ b/.gitignore @@ -151,3 +151,9 @@ modules.xml **/flutter_export_environment.sh /example/ios/Flutter/Flutter.podspec + +packages/**/pubspec_overrides.yaml +./pubspec_overrides.yaml +/example/pubspec_overrides.yaml + +coverage/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9195d6ea65..13a7fda044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,144 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +#### 3.0.0 *March 2025* + + - Graduate package to a stable release. See pre-releases prior to this version for changelog entries. + +#### 3.0.0-beta.2 *May 2023* + + - Several Breaking Changes. See the [migration guide](https://github.com/Sub6Resources/flutter_html/wiki/Migration-Guides#300) + + - **FIX**: start list items on a new line ([#1281](https://github.com/sub6resources/flutter_html/issues/1281)). ([496d1aa8](https://github.com/sub6resources/flutter_html/commit/496d1aa8e655891d2f597c5e4d7e92057801d815)) + - **FIX**: Add "display: Display.block" to table ([#1278](https://github.com/sub6resources/flutter_html/issues/1278)). ([6350f023](https://github.com/sub6resources/flutter_html/commit/6350f02354b7de601ce294123717e2051be97eee)) + - **FIX**: improve API for ExtensionContext and export marker.dart ([#1273](https://github.com/sub6resources/flutter_html/issues/1273)). ([27e33a95](https://github.com/sub6resources/flutter_html/commit/27e33a955e872d47306db9480f74f6da2e9a028a)) + - **FIX**: Cleaned up whitespace processing and added whitespace tests ([#1267](https://github.com/sub6resources/flutter_html/issues/1267)). ([cc00406b](https://github.com/sub6resources/flutter_html/commit/cc00406b1d0c115e5c66dd4bdfb40db32496f55f)) + - **FIX**: a tag should not style as link if href is not provided ([#1265](https://github.com/sub6resources/flutter_html/issues/1265)). ([d7247cb3](https://github.com/sub6resources/flutter_html/commit/d7247cb303c25d0011f85f9b2d3687924de3d83d)) + - **FEAT**: Update CssBoxWidget to handle rtl marker boxes ([#1270](https://github.com/sub6resources/flutter_html/issues/1270)). ([d7091990](https://github.com/sub6resources/flutter_html/commit/d7091990d193e892e2f782ac8d91fc0326aff4bc)) + - **FEAT**: support vertical-align in inline styles ([#1266](https://github.com/sub6resources/flutter_html/issues/1266)). ([fe896de5](https://github.com/sub6resources/flutter_html/commit/fe896de5ed8b79425bb33800a26fa4ac328057fe)) + - **FEAT**: Add WrapperExtension helper ([#1264](https://github.com/sub6resources/flutter_html/issues/1264)). ([2ffa1dda](https://github.com/sub6resources/flutter_html/commit/2ffa1ddabb3f2a660ab85c551255b89fe8a24ab5)) + + +#### 3.0.0-beta.1 - *May 2023* + + - Several Breaking Changes. See the [migration guide](https://github.com/Sub6Resources/flutter_html/wiki/Migration-Guides#300) + + - **FIX**: Aspect ratio exception when height is 0 ([#1222](https://github.com/sub6resources/flutter_html/issues/1222)). ([ed75f8fe](https://github.com/sub6resources/flutter_html/commit/ed75f8fef779e920ecc1f27719a4150a29e3ebee)) + - **FIX**: Fix issue with font scaling introduced in 3.0.0-alpha.6 ([#1173](https://github.com/sub6resources/flutter_html/issues/1173)). ([c75e0dfb](https://github.com/sub6resources/flutter_html/commit/c75e0dfb1be6cb79748f719487043d12bc330c60)) + - **FIX**: Fix various issues with list rendering. ([520ff3c3](https://github.com/sub6resources/flutter_html/commit/520ff3c326d5dc8f5a601022c2a32d58e2e83cbb)) + - **FIX**: Apply margins to properly. ([7581ea79](https://github.com/sub6resources/flutter_html/commit/7581ea798744b2830affaaf75bbdff016b03f7af)) + - **FIX**: Use enum instead of const int internally in length.dart. ([9dc7f08c](https://github.com/sub6resources/flutter_html/commit/9dc7f08ca238ff6a93314be5de716ad4e3baebb8)) + - **FIX**: Change CSSBoxWidget to CssBoxWidget. ([a62449a7](https://github.com/sub6resources/flutter_html/commit/a62449a77c18701a0faf8ffd650f9c535b2d006c)) + - **FEAT**: Support mmultiscripts. ([#1175](https://github.com/sub6resources/flutter_html/issues/1175)). ([a999a300](https://github.com/sub6resources/flutter_html/commit/a999a30027eff0aabb2825ffdbe383f9affab7f6)) + - **FEAT**: Support mfenced. ([#1174](https://github.com/sub6resources/flutter_html/issues/1174)). ([9ca23084](https://github.com/sub6resources/flutter_html/commit/9ca230848beb15332f96294083ed4989831130d7)) + - **FEAT**: Upgrade list-style-type to CSS3. ([deb726ae](https://github.com/sub6resources/flutter_html/commit/deb726ae2776f45305026c0aa081d4a5b5a1c71d)) + - **FEAT**: Support mtable, mtd, mtr for draw matrix. ([#1164](https://github.com/sub6resources/flutter_html/issues/1164)). ([e99e2cc1](https://github.com/sub6resources/flutter_html/commit/e99e2cc1553ab17b4ceff08f784e99283b28dff4)) + +## 3.0.0-alpha.6 - *September 2022* + + - **FIX** #731 Resolve newline `
` issue + - **FIX** Align the baseline of inline content with the baseline of its parent flow, even if it has padding or borders + - **FIX** Improved fontSize inheritance when cascading styles + - **FIX** `auto` margins now work for any `Display.BLOCK` element. + - **FIX** `auto` width and height is now the default, rather than `null` + - **FIX** New CSSBoxWidget that handles calculations of child sizes for a more accurate HTML/CSS layout + - **BREAKING** New `Margin`, `Height`, and `Width` classes that allow `em`, `rem`, `px`, `auto`, and `%` values to be given + - **FEAT** Negative margins are now allowed + - **FIX** Updated default `p` and `h1-6` styles to use `em` for better font scaling + - **BREAKING** Package now requires Dart sdk >= Dart 2.17 + - **FIX**: Apply margins to
properly. (7581ea79) + - **FIX**: Use enum instead of const int internally in length.dart. (9dc7f08c) + - **FIX**: Change CSSBoxWidget to CssBoxWidget. (a62449a7) + - **FIX**: fix textShadow color declaration handler. (77ffe7cb) + - **FIX**: ol use default style. (1c2412a2) + - **FIX**: Crash when a tr tag includes text node. (ba8301c9) + - **FEAT**: exposes fontFamilyFallback parameter. (1d65aafd) + +## [3.0.0-alpha.5] - June 9, 2022: +* Fixed hot reloads, thanks @arjenmels +* Fixed link taps not working +* Improvements in README + +## [3.0.0-alpha.3] - April 14, 2022: +* Fixed styling not being applied to list item markers +* [video] Fixed crash when iframe or video tags used unsupported/incorrect height or width + +## [3.0.0-alpha.2] - January 5, 2022: +* **BREAKING** Full modularization using split packages; see our upgrade guide or use flutter_html_all + +## [3.0.0-alpha.1] - December 21, 2021: +* **BREAKING** Reworked custom renders pending full modularation in 3.0.0 +* Extended support custom render when using SelectableHtml +* Updated flutter_svg to 1.0.0 +* Support flutter_webview 3.x +* Automatic disposal of video and audio controllers +* Fix block elements bottom spacing in table cells + +## [2.2.1] - December 8, 2021: +* Allow styling on ruby tags +* Allow width/height/alignment styling on table/tr/td tags +* Prevent images causing rebuilding and leaking memory +* Fixes display of list items on iOS with font weights below 400 +* Prevent crash on negative margins or paddings + +## [2.2.0] - November 29, 2021: +* Explicitly declare multiplatform support +* Extended and fixed list-style (marker) support +* Basic support for height/width css properties +* Support changing scroll physics of SelectableText.rich +* Support text transform css property +* Bumped minimum flutter_math_fork version for Flutter 2.5 compatibility +* Fix styling of iframes +* Fix nested font tag application +* Fix whitespace rendering between list items +* Prevent crash on empty
tag and tables with both colspan/rowspan +* Prevent crash on use of negative margins in css + +## [2.1.5] - October 7, 2021: +* Ignore unsupported custom style selectors when using fromCss +* Fix SVG tag usage inside tables +* Properly fix regression in usage of line breaks + +## [2.1.4] - October 3, 2021: +* Fix regression in usage of line breaks in body being stripped + +## [2.1.3] - October 1, 2021: +* Update minimum versions of dependencies for Flutter 2.5 compatibility +* Extended and fixed support for css shadow +* Fix block tags with explicit whitespace from being stripped + +## [2.1.2] - September 2, 2021: +* Allow setting selectionControls with SelectableHtml +* Fix onLinkTap not working with SelectableHtml +* Don't crash when parsing unsupported :hover +* Prevent endless loading when using animated images + +## [2.1.1] - July 28, 2021: +* Stable release with all 2.1.1-preview.X changes + +## [2.1.1-preview.0] - July 27, 2021: +* Improves hr tag support +* Fixes a leading whitespace issue +* Fixes some crashes with CSS parsing + +## [2.1.0] - June 3, 2021: +* SelectableHtml widget (supporting a subset of tags) which allow text selection +* Fixed shrinkWrap to actually shrink the horizontal space +* Support style tags to apply styling from inline css +* Support applying styles from Flutter themes +* Mouse hover on links when using Flutter Web +* Allow custom anchor link tap implementations +* Support additional list styling options +* Fix several minor whitespace issues in text flow +* Fixed specific colspan/rowspan usages in tables +* Fixed whitespace issues around images +* Swallow duplicate ids to prevent crashing the widget +* Fixes crashing tap detection when using both link and image taps +* Updates external dependencies +* Raised minimum Flutter version to 2.2.0 + ## [2.0.0] - April 29, 2021: * Stable release with all 2.0.0-nullsafety.X changes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2dc1383211..02466657d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,8 +1,3 @@ Thanks for your interest in contributing to `flutter_html`! -I'm pretty busy, so in order to help me best make use of the time I spend working on this project, please adhere to the following guidelines when contributing: - -1. In general, don't submit a pull request without discussing the feature with me, in an issue, first. I don't want you to have to do a whole bunch of work for nothing. This also makes it so there won't be a whole bunch of changes I make you do in order to have your pull request merged. -2. Please read the [wiki](https://github.com/Sub6Resources/flutter_html/wiki) before contributing (there are only two pages at the moment). This will give you an idea of what my plans are for this repository, and what you do and don't need to work on. - -More specific guidelines will be added soon. +Please see the contribution guide in the wiki: https://github.com/Sub6Resources/flutter_html/wiki/Contributing diff --git a/LICENSE b/LICENSE index b0f300d959..230b35b47f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Matthew Whitaker +Copyright (c) 2019-2025 The flutter_html developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index d464ba287b..adea04a977 100644 --- a/README.md +++ b/README.md @@ -1,139 +1,93 @@ # flutter_html [![pub package](https://img.shields.io/pub/v/flutter_html.svg)](https://pub.dev/packages/flutter_html) [![codecov](https://codecov.io/gh/Sub6Resources/flutter_html/branch/master/graph/badge.svg)](https://codecov.io/gh/Sub6Resources/flutter_html) -[![CircleCI](https://circleci.com/gh/Sub6Resources/flutter_html.svg?style=svg)](https://circleci.com/gh/Sub6Resources/flutter_html) +[![GitHub Actions](https://github.com/Sub6Resources/flutter_html/actions/workflows/test.yml/badge.svg)](https://github.com/Sub6Resources/flutter_html/actions) [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/Sub6Resources/flutter_html/blob/master/LICENSE) A Flutter widget for rendering HTML and CSS as Flutter widgets. -
- - - - - - - - - - -
Screenshot 1Screenshot 2Screenshot 3
A Screenshot of flutter_htmlAnother Screenshot of flutter_htmlYet another Screenshot of flutter_html
+```dart +Widget build(context) { + return Html( + data: """ +

Hello, World!

+

flutter_html supports a variety of HTML and CSS tags and attributes.

+

Over a hundred static tags are supported out of the box.

+

Or you can even define your own using an Extension:

+

Its easy to add custom styles to your Html as well using the Style class:

+

Here's a fancy <p> element!

+ """, + extensions: [ + TagExtension( + tagsToExtend: {"flutter"}, + child: const FlutterLogo(), + ), + ], + style: { + "p.fancy": Style( + textAlign: TextAlign.center, + padding: const EdgeInsets.all(16), + backgroundColor: Colors.grey, + margin: Margins(left: Margin(50, Unit.px), right: Margin.auto()), + width: Width(300, Unit.px), + fontWeight: FontWeight.bold, + ), + }, + ); +} +``` -## Table of Contents: +becomes... -- [Installing](#installing) +A screenshot showing the above code snippet rendered using flutter_html -- [Currently Supported HTML Tags](#currently-supported-html-tags) +## Table of Contents: -- [Currently Supported CSS Attributes](#currently-supported-css-attributes) +- [Supported HTML Tags](https://github.com/Sub6Resources/flutter_html/wiki/Supported-HTML-Elements) -- [Currently Supported Inline CSS Attributes](#currently-supported-inline-css-attributes) +- [Supported CSS Attributes](https://github.com/Sub6Resources/flutter_html/wiki/Supported-CSS-Attributes) - [Why flutter_html?](#why-this-package) +- [Migration Guide](#migration-guides) + - [API Reference](#api-reference) - [Constructors](#constructors) - [Parameters Table](#parameters) - - [Getters](#getters) - - - [Data](#data) - - - [Document](#document) - - - [onLinkTap](#onlinktap) - - - [customRender](#customrender) - - - [onImageError](#onimageerror) - - - [onMathError](#onmatherror) - - - [onImageTap](#onimagetap) - - - [tagsList](#tagslist) - - - [style](#style) - - - [navigationDelegateForIframe](#navigationdelegateforiframe) - - - [customImageRender](#customimagerender) - - - [typedef ImageSourceMatcher (with examples)](#typedef-imagesourcematcher) - - - [typedef ImageRender (with examples)](#typedef-imagerender) - - - [Extended examples](#example-usages---customimagerender) - -- [Rendering Reference](#rendering-reference) - - - [Image](#image) - - - [Iframe](#iframe) +- [External Packages](#external-packages) - - [Audio](#audio) + - [`flutter_html_all`](#flutter_html_all) - - [Video](#video) + - [`flutter_html_audio`](#flutter_html_audio) - - [SVG](#svg) + - [`flutter_html_iframe`](#flutter_html_iframe) - - [MathML](#mathml) + - [`flutter_html_math`](#flutter_html_math) - - [Tex](#tex) + - [`flutter_html_svg`](#flutter_html_svg) - - [Table](#table) - -- [Notes](#notes) - -- [Migration Guide](#migration-guides) - -- [Contribution Guide](#contribution-guide) - -## Installing: + - [`flutter_html_table`](#flutter_html_table) -Add the following to your `pubspec.yaml` file: - - dependencies: - flutter_html: ^2.0.0 + - [`flutter_html_video`](#flutter_html_video) + +- [Frequently Asked Questions](#faq) -## Currently Supported HTML Tags: -| | | | | | | | | | | | -|------------|-----------|-------|-------------|---------|---------|-------|------|--------|--------|--------| -|`a` | `abbr` | `acronym`| `address` | `article`| `aside` | `audio`| `b` | `bdi` | `bdo` | `big` | -|`blockquote`| `body` | `br` | `caption` | `cite` | `code` | `data`| `dd` | `del` | `details` | `dfn` | -| `div` | `dl` | `dt` | `em` | `figcaption`| `figure`| `footer`| `h1` | `h2` | `h3` | `h4` | -| `h5` |`h6` | `header` | `hr` | `i` | `iframe`| `img` | `ins` | `kbd`| `li` | `main` | -| `mark` | `nav` | `noscript`|`ol` | `p` | `pre` | `q` | `rp` | `rt` | `ruby` | `s` | -| `samp` | `section` | `small` | `span`| `strike` | `strong`| `sub` | `sup` | `summary` | `svg`| `table`| -| `tbody` | `td` | `template` | `tfoot` | `th` | `thead` |`time` | `tr` | `tt` | `u` | `ul` | -| `var` | `video` | `math`: | `mrow` | `msup` | `msub` | `mover` | `munder` | `msubsup` | `moverunder` | `mfrac` | -| `mlongdiv` | `msqrt` | `mroot` | `mi` | `mn` | `mo` | | | | | | +- [Example](#example) - -## Currently Supported CSS Attributes: -| | | | | | | | -|------------------|--------|------------|----------|--------------|------------------------|------------| -|`background-color`| `color`| `direction`| `display`| `font-family`| `font-feature-settings`| `font-size`| -|`font-style` | `font-weight`| `height` | `letter-spacing`| `line-height`| `list-style-type` | `list-style-position`| -|`padding` | `margin`| `text-align`| `text-decoration`| `text-decoration-color`| `text-decoration-style`| `text-decoration-thickness`| -|`text-shadow` | `vertical-align`| `white-space`| `width` | `word-spacing`| | | - -## Currently Supported Inline CSS Attributes: -| | | | | | | | -|------------------|--------|------------|----------|--------------|------------------------|------------| -|`background-color`| `border` | `color`| `direction`| `display`| `font-family`| `font-feature-settings` | -| `font-size`|`font-style` | `font-weight`| `line-height` | `list-style-type` | `list-style-position`|`padding` | -| `margin`| `text-align`| `text-decoration`| `text-decoration-color`| `text-decoration-style`| `text-shadow` | | - -Don't see a tag or attribute you need? File a feature request or contribute to the project! ## Why this package? This package is designed with simplicity in mind. Originally created to allow basic rendering of HTML content into the Flutter widget tree, this project has expanded to include support for basic styling as well! -If you need something more robust and customizable, the package also provides a number of optional custom APIs for extremely granular control over widget rendering! + +If you need something more robust and customizable, the package also provides a number of extension APIs for extremely granular control over widget rendering! + +## Migration Guides + +[3.0.0 Migration Guide](https://github.com/Sub6Resources/flutter_html/wiki/Migration-Guides#300) ## API Reference: @@ -143,7 +97,7 @@ For a full example, see [here](https://github.com/Sub6Resources/flutter_html/tre Below, you will find brief descriptions of the parameters the`Html` widget accepts and some code snippets to help you use this package. -## Constructors: +### Constructors: The package currently has two different constructors - `Html()` and `Html.fromDom()`. @@ -151,672 +105,253 @@ The `Html()` constructor is for those who would like to directly pass HTML from If you would like to modify or sanitize the HTML before rendering it, then `Html.fromDom()` is for you - you can convert the HTML string to a `Document` and use its methods to modify the HTML as you wish. Then, you can directly pass the modified `Document` to the package. This eliminates the need to parse the modified `Document` back to a string, pass to `Html()`, and convert back to a `Document`, thus cutting down on load times. -### Parameters: - -| Parameters | Description | -|--------------|-----------------| -| `data` | The HTML data passed to the `Html` widget. This is required and cannot be null when using `Html()`. | -| `document` | The DOM document passed to the `Html` widget. This is required and cannot be null when using `Html.fromDom()`. | -| `onLinkTap` | A function that defines what the widget should do when a link is tapped. The function exposes the `src` of the link as a `String` to use in your implementation. | -| `customRender` | A powerful API that allows you to customize everything when rendering a specific HTML tag. | -| `onImageError` | A function that defines what the widget should do when an image fails to load. The function exposes the exception `Object` and `StackTrace` to use in your implementation. | -| `omMathError` | A function that defines what the widget should do when a math fails to render. The function exposes the parsed Tex `String`, as well as the error and error with type from `flutter_math` as a `String`. | -| `shrinkWrap` | A `bool` used while rendering different widgets to specify whether they should be shrink-wrapped or not, like `ContainerSpan` | -| `onImageTap` | A function that defines what the widget should do when an image is tapped. The function exposes the `src` of the image as a `String` to use in your implementation. | -| `tagsList` | A list of elements the `Html` widget should render. The list should contain the tags of the HTML elements you wish to include. | -| `style` | A powerful API that allows you to customize the style that should be used when rendering a specific HTMl tag. | -| `navigationDelegateForIframe` | Allows you to set the `NavigationDelegate` for the `WebView`s of all the iframes rendered by the `Html` widget. | -| `customImageRender` | A powerful API that allows you to fully customize how images are loaded. | +### Parameters: -### Getters: +| Parameters | Description | +|------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `data` | The HTML data passed to the `Html` widget. This is required and cannot be null when using `Html()`. | +| `document` | The DOM document passed to the `Html` widget. This is required and cannot be null when using `Html.fromDom()`. | +| `onLinkTap` | Optional. A function that defines what the widget should do when a link is tapped. The function exposes the `src` of the link as a `String` to use in your implementation. | +| `extensions` | Optional. A powerful API that allows you to customize everything when rendering a specific HTML tag. | +| `shrinkWrap` | Optional. A `bool` used while rendering different widgets to specify whether they should be shrink-wrapped or not, like `ContainerSpan` | +| `onlyRenderTheseTags` | Optional. An exclusive set of elements the `Html` widget should render. Note that your html will be wrapped in `` and `` if it isn't already, so you should include those in this list. | +| `doNotRenderTheseTags` | Optional. A set of tags that should not be rendered by the `Html` widget. | +| `style` | Optional. A powerful API that allows you to customize the style that should be used when rendering a specific HTMl tag. | -Currently the only getter is `Html.tags`. This provides a list of all the tags the package renders. The main use case is to assist in blacklisting elements using `tagsList`. See an [example](#example-usage---tagslist---excluding-tags) below. -### Data: - -The HTML data passed to the `Html` widget as a `String`. This is required and cannot be null when using `Html`. -Any HTML tags in the `String` that are not supported by the package will not be rendered. - -#### Example Usage - Data: - -```dart -Widget html = Html( - data: """
-

Demo Page

-

This is a fantastic product that you should buy!

-

Features

- - -
""", -); -``` - -### Document: - -The DOM document passed to the `Html` widget as a `Document`. This is required and cannot be null when using `Html.fromDom()`. -Any HTML tags in the document that are not supported by the package will not be rendered. -Using the `Html.fromDom()` constructor can be useful when you would like to sanitize the HTML string yourself before passing it to the package. - -#### Example Usage - Document: - -```dart -import 'package:html/parser.dart' as htmlparser; -import 'package:html/dom.dart' as dom; -... -String htmlData = """
-

Demo Page

-

This is a fantastic product that you should buy!

-

Features

- - -
"""; -dom.Document document = htmlparser.parse(htmlData); -/// sanitize or query document here -Widget html = Html( - document: document, -); -``` +More examples and in-depth details are available: -### onLinkTap: + - [Style](https://github.com/Sub6Resources/flutter_html/wiki/How-To-Use-Style). + - [HtmlExtension](https://github.com/Sub6Resources/flutter_html/wiki/How-To-Use-Extensions) -A function that defines what the widget should do when a link is tapped. +## External Packages -#### Example Usage - onLinkTap: +### `flutter_html_all` -```dart -Widget html = Html( - data: """

- Linking to websites has never been easier. -

""", - onLinkTap: (String? url, RenderContext context, Map attributes, dom.Element? element) { - //open URL in webview, or launch URL in browser, or any other logic here - } -); -``` +This package is simply a convenience package that exports all the other external packages below. You should use this if you plan to render all the tags that require external dependencies. -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`. +### `flutter_html_audio` -### customRender: +This package renders audio elements using the [`chewie_audio`](https://pub.dev/packages/chewie_audio) and the [`video_player`](https://pub.dev/packages/video_player) plugin. -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! +The package considers the attributes `controls`, `loop`, `src`, `autoplay`, `width`, and `muted` when rendering the audio widget. -`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,. +#### Adding the `AudioHtmlExtension`: -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`. +Add the dependency to your pubspec.yaml: -#### Example Usages - customRender: -1. Simple example - rendering custom HTML tags + flutter pub add flutter_html_audio ```dart -Widget html = Html( - data: """ -

Display bird element and flutter element

- - - """, - customRender: { - "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, - ); - }, - }, -); -``` - -2. Complex example - wrapping the default widget with your own, in this case placing a horizontal scroll around a (potentially too wide) table. +import 'package:flutter_html_audio/flutter_html_audio.dart'; -
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), - ); - } - }, + data: myHtml, + extensions: [ + AudioHtmlExtension(), + ], ); ``` -3. Complex example - rendering an `iframe` differently based on whether it is an embedded youtube video or some other embedded content. +### `flutter_html_iframe` -
View code +This package renders iframes using the [`webview_flutter`](https://pub.dev/packages/webview_flutter) plugin. -```dart -Widget html = Html( - data: """ -

Google iframe:

- -

YouTube iframe:

- - """, - customRender: { - "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: 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: 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 (attrs['src'] != null && attrs['src']!.contains("youtube.com/embed")) { - if (!request.url.contains("youtube.com/embed")) { - return NavigationDecision.prevent; - } else { - return NavigationDecision.navigate; - } - } else { - return NavigationDecision.navigate; - } - }, - ), - ); - } else { - return Container(height: 0); - } - } - } - ); -``` -
+When rendering iframes, the package considers the width, height, and sandbox attributes. -More example usages and in-depth details available [here](https://github.com/Sub6Resources/flutter_html/wiki/All-About-customRender). +Sandbox controls the JavaScript mode of the webview - a value of `null` or `allow-scripts` will set `javascriptMode: JavascriptMode.unrestricted`, otherwise it will set `javascriptMode: JavascriptMode.disabled`. -### onImageError: +#### Adding the `IframeHtmlExtension`: -A function that defines what the widget should do when an image fails to load. The function exposes the exception `Object` and `StackTrace` to use in your implementation. +Add the dependency to your pubspec.yaml: -#### Example Usage - onImageError: + flutter pub add flutter_html_iframe ```dart -Widget html = Html( - data: """Alt Text of an intentionally broken image""", - onImageError: (Exception exception, StackTrace stackTrace) { - FirebaseCrashlytics.instance.recordError(exception, stackTrace); - }, -); -``` +import 'package:flutter_html_iframe/flutter_html_iframe.dart'; -### onMathError: - -A function that defines what the widget should do when a math fails to render. The function exposes the parsed Tex `String`, as well as the error and error with type from `flutter_math` as a `String`. - -#### Example Usage - onMathError: - -```dart Widget html = Html( - data: """""", - onMathError: (String parsedTex, String error, String errorWithType) { - //your logic here. A Widget must be returned from this function: - return Text(error); - //you can also try and fix the parsing yourself: - return Math.tex(correctedParsedTex); - }, + data: myHtml, + extensions: [ + IframeHtmlExtension(), + ], ); ``` -### onImageTap: +You can set the `navigationDelegate` of the webview with the `navigationDelegate` property on `IframeHtmlExtension`. This allows you to block or allow the loading of certain URLs. -A function that defines what the widget should do when an image is tapped. +### `flutter_html_math` -#### Example Usage - onImageTap: +This package renders MathML elements using the [`flutter_math_fork`](https://pub.dev/packages/flutter_math_fork) plugin. -```dart -Widget html = Html( - data: """Google""", - onImageTap: (String? url, RenderContext context, Map attributes, dom.Element? element) { - //open image in webview, or launch image in browser, or any other logic here - } -); -``` +When rendering MathML, the package takes the MathML data within the `` tag and tries to parse it to Tex. Then, it will pass the parsed string to `flutter_math_fork`. -### tagsList: +Because this package is parsing MathML to Tex, it may not support some functionalities. The current list of supported tags can be found [on the Wiki](https://github.com/Sub6Resources/flutter_html/wiki/First-Party-Extensions#flutter_html_math), but some of these only have partial support at the moment. -A list of elements the `Html` widget should render. The list should contain the tags of the HTML elements you wish to whitelist. +#### Adding the `MathHtmlExtension`: -#### Example Usage - tagsList - Excluding Tags: -You may have instances where you can choose between two different types of HTML tags to display the same content. In the example below, the `

Render this item

- Do not render this item or any other item - - """, - tagsList: ['p'] + data: myHtml, + extensions: [ + MathHtmlExtension(), + ], ); ``` -Here, the package will only ever render `

` and ignore all other tags. +If the parsing errors, you can use the `onMathErrorBuilder` property of `MathHtmlException` to catch the error and potentially fix it on your end. -### style: +The function exposes the parsed Tex `String`, as well as the error and error with type from `flutter_math_fork` as a `String`. -A powerful API that allows you to customize the style that should be used when rendering a specific HTMl tag. +You can analyze the error and the parsed string, and finally return a new instance of `Math.tex()` with the corrected Tex string. -`style` accepts a `Map`. The `Style` type is a class that allows you to set all the CSS styling the package currently supports. See [here](https://pub.dev/documentation/flutter_html/latest/style/Style-class.html) for the full list. +#### Tex -To use this API, set the key as the tag of the HTML element you wish to provide a custom implementation for, and set the value to be a `Style` with your customizations. +If you have a Tex string you'd like to render inside your HTML you can do that using the same [`flutter_math_fork`](https://pub.dev/packages/flutter_math_fork) plugin. -#### Example Usage - style: +Use a custom tag inside your HTML (an example could be ``), and place your **raw** Tex string inside. + +Then, use the `extensions` parameter to add the widget to render Tex. It could look like this: ```dart -Widget html = Html( - data: """ -

Table support:

- - - - - - - - - - - - - - - - - - - -
OneTwoThree
Rowspan\nRowspan\nRowspan\nRowspan\nRowspan\nRowspan\nRowspan\nRowspan\nRowspan\nRowspanDataData
Google
fDatafDatafData
""", - style: { - // tables will have the below background color - "table": Style( - backgroundColor: Color.fromARGB(0x50, 0xee, 0xee, 0xee), - ), - // some other granular customizations are also possible - "tr": Style( - border: Border(bottom: BorderSide(color: Colors.grey)), - ), - "th": Style( - padding: EdgeInsets.all(6), - backgroundColor: Colors.grey, - ), - "td": Style( - padding: EdgeInsets.all(6), - alignment: Alignment.topLeft, +Widget htmlWidget = Html( + data: r"""i\hbar\frac{\partial}{\partial t}\Psi(\vec x,t) = -\frac{\hbar}{2m}\nabla^2\Psi(\vec x,t)+ V(\vec x)\Psi(\vec x,t)""", + extensions: [ + TagExtension( + tagsToExtend: {"tex"}, + builder: (extensionContext) { + return Math.tex( + extensionContext.innerHtml, + mathStyle: MathStyle.display, + textStyle: extensionContext.styledElement?.style.generateTextStyle(), + onErrorFallback: (FlutterMathException e) { + //optionally try and correct the Tex string here + return Text(e.message); + }, + ); + } ), - // text that renders h1 elements will be red - "h1": Style(color: Colors.red), - } + ], ); ``` -More examples and in-depth details available [here](https://github.com/Sub6Resources/flutter_html/wiki/Style). +### `flutter_html_svg` -### navigationDelegateForIframe: - -Allows you to set the `NavigationDelegate` for the `WebView`s of all the iframes rendered by the `Html` widget. You can block or allow the loading of certain URLs with the `NavigationDelegate`. - -#### Example Usage - navigationDelegateForIframe: +This package renders svg elements using the [`flutter_svg`](https://pub.dev/packages/flutter_svg) plugin. -```dart -Widget html = Html( - data: """ -

YouTube iframe:

- -

Google iframe:

- - """, - navigationDelegateForIframe: (NavigationRequest request) { - if (request.url.contains("google.com/images")) { - return NavigationDecision.prevent; - } else { - return NavigationDecision.navigate; - } - }, -); -``` +When rendering SVGs, the package takes the SVG data within the `` tag and passes it to `flutter_svg`. The `width` and `height` attributes are considered while rendering, if given. -### customImageRender: +The package also exposes a few ways to render SVGs within an `` tag, specifically base64 SVGs, asset SVGs, and network SVGs. -A powerful API that allows you to customize what the `Html` widget does when rendering an image, down to the most minute detail. +#### Adding the `SvgHtmlExtension`: -`customImageRender` accepts a `Map`. `ImageSourceMatcher` provides the matching function, while `ImageRender` provides the widget to be rendered. +Add the dependency to your pubspec.yaml: -The default image renders are: + flutter pub add flutter_html_svg ```dart -final Map defaultImageRenders = { - base64UriMatcher(): base64ImageRender(), - assetUriMatcher(): assetImageRender(), - networkSourceMatcher(extension: "svg"): svgNetworkImageRender(), - networkSourceMatcher(): networkImageRender(), -}; -``` - -See [the source code](https://github.com/Sub6Resources/flutter_html/blob/master/lib/image_render.dart) for details on how these are implemented. - -When setting `customImageRenders`, the package will prioritize the custom renders first, while the default ones are used as a fallback. +import 'package:flutter_html_svg/flutter_html_svg.dart'; -Note: Order is very important when you set `customImageRenders`. The more specific your `ImageSourceMatcher`, the higher up in the `customImageRender` list it should be. - -#### typedef ImageSourceMatcher - -This is type defined as a function that passes the attributes as a `Map` and the DOM element as `dom.Element`. This type is used to define how an image should be matched i.e. whether the package should override the default rendering method and instead use your custom implementation. - -A typical usage would look something like this: - -```dart -ImageSourceMatcher base64UriMatcher() => (attributes, element) => - attributes["src"] != null && - attributes["src"]!.startsWith("data:image") && - attributes["src"]!.contains("base64,"); +Widget html = Html( + data: myHtml, + extensions: [ + SvgHtmlExtension(), + ], +); ``` -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. - -You can also declare your own variables in the function itself, which would look like this: +### `flutter_html_table` -```dart -ImageSourceMatcher networkSourceMatcher({ -/// all three are optional, you don't need to have these in the function - List schemas: const ["https", "http"], - List domains: const ["your domain 1", "your domain 2"], - String extension: "your extension", -}) => - (attributes, element) { - final src = Uri.parse(attributes["src"] ?? "about:blank"); - return schemas.contains(src.scheme) && - domains.contains(src.host) && - src.path.endsWith(".$extension"); - }; -``` +This package renders table elements using the [`flutter_layout_grid`](https://pub.dev/packages/flutter_layout_grid) plugin. -In the above example, the possible schemas are checked against the scheme of the `src`, and optionally the domains and extensions are also checked. This implementation allows for extremely granular control over what images are matched, and could even be changed on the fly with a variable. +When rendering table elements, the package tries to calculate the best fit for each element and size its cell accordingly. `Rowspan`s and `colspan`s are considered in this process, so cells that span across multiple rows and columns are rendered as expected. Heights are determined intrinsically to maintain an optimal aspect ratio for the cell. -#### typedef ImageRender +#### Adding the `TableHtmlExtension`: -This is a type defined as a function that passes the attributes of the image as a `Map`, the current [`RenderContext`](https://github.com/Sub6Resources/flutter_html/wiki/All-About-customRender#rendercontext-context), and the DOM element as `dom.Element`. This type is used to define the widget that should be rendered when used in conjunction with an `ImageSourceMatcher`. +Add the dependency to your pubspec.yaml: -A typical usage might look like this: + flutter pub add flutter_html_table ```dart -ImageRender base64ImageRender() => (context, attributes, element) { - final decodedImage = base64.decode(attributes["src"] != null ? - attributes["src"].split("base64,")[1].trim() : "about:blank"); - return Image.memory( - decodedImage, - ); - }; -``` - -The above example should be used with the `base64UriMatcher()` in the examples for `ImageSourceMatcher`. +import 'package:flutter_html_table/flutter_html_table.dart'; -Just like functions for `ImageSourceMatcher`, you can declare your own variables in the function itself: - -```dart -ImageRender networkImageRender({ - Map headers, - double width, - double height, - Widget Function(String) altWidget, -}) => - (context, attributes, element) { - return Image.network( - attributes["src"] ?? "about:blank", - headers: headers, - width: width, - height: height, - frameBuilder: (ctx, child, frame, _) { - if (frame == null) { - return altWidget.call(attributes["alt"]) ?? - Text(attributes["alt"] ?? "", - style: context.style.generateTextStyle()); - } - return child; - }, - ); - }; +Widget html = Html( + data: myHtml, + extensions: [ + TableHtmlExtension(), + ], +); ``` -Implementing these variables allows you to customize every last detail of how the widget is rendered. +### `flutter_html_video` -#### Example Usages - customImageRender: +This package renders video elements using the [`chewie`](https://pub.dev/packages/chewie) and the [`video_player`](https://pub.dev/packages/video_player) plugin. -`customImageRender` can be used in two different ways: +The package considers the attributes `controls`, `loop`, `src`, `autoplay`, `poster`, `width`, `height`, and `muted` when rendering the video widget. -1. Overriding a default render: -```dart -Widget html = Html( - data: """ - Flutter
- Google
- """, - customImageRenders: { - networkSourceMatcher(domains: ["flutter.dev"]): - (context, attributes, element) { - return FlutterLogo(size: 36); - }, - networkSourceMatcher(): networkImageRender( - headers: {"Custom-Header": "some-value"}, - altWidget: (alt) => Text(alt ?? ""), - loadingWidget: () => Text("Loading..."), - ), - (attr, _) => attr["src"] != null && attr["src"]!.startsWith("/wiki"): - networkImageRender( - mapUrl: (url) => "https://upload.wikimedia.org" + url), - }, -); -``` +#### Adding the `VideoHtmlExtension`: -Above, there are three custom `networkSourceMatcher`s, which will be applied - in order - before the default implementations. +Add the dependency to your pubspec.yaml: -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. + flutter pub add flutter_html_video -2. Creating your own renders: ```dart -ImageSourceMatcher classAndIdMatcher({String classToMatch, String idToMatch}) => (attributes, element) => - attributes["class"] != null && attributes["id"] != null && - (attributes["class"]!.contains(classToMatch) || - attributes["id"]!.contains(idToMatch)); - -ImageRender classAndIdRender({String classToMatch, String idToMatch}) => (context, attributes, element) { - if (attributes["class"] != null && attributes["class"]!.contains(classToMatch)) { - return Image.asset(attributes["src"] ?? "about:blank"); - } else { - return Image.network( - attributes["src"] ?? "about:blank", - semanticLabel: attributes["longdesc"] ?? "", - width: attributes["width"], - height: attributes["height"], - color: context.style.color, - frameBuilder: (ctx, child, frame, _) { - if (frame == null) { - return Text(attributes["alt"] ?? "", style: context.style.generateTextStyle()); - } - return child; - }, - ); - } -}; +import 'package:flutter_html_video/flutter_html_video.dart'; Widget html = Html( - data: """ - alt text
- alt text 2
- """, - customImageRenders: { - classAndIdMatcher(classToMatch: "class1", idToMatch: "imageId"): classAndIdRender(classToMatch: "class1", idToMatch: "imageId") - }, + data: myHtml, + extensions: [ + VideoHtmlExtension(), + ], ); ``` -The above example has a matcher that checks for either a class or an id, and then returns two different widgets based on whether a class was matched or an id was matched. - -The sky is the limit when using the custom image renders. You can make it as granular as you want, or as all-encompassing as you want, and you have full control of everything. Plus you get the package's style parsing to use in your custom widgets, so your code looks neat and readable! - -## Rendering Reference - -This section will describe how certain HTML elements are rendered by this package, so you can evaluate how your HTML will be rendered and structure it accordingly. - -### Image - -This package currently has support for base64 images, asset images, network SVGs inside an ``, and network images. - -The package uses the `src` of the image to determine which of the above types to render. The order is as follows: -1. If the `src` is null, render the alt text of the image, if any. -2. If the `src` starts with "data:image" and contains "base64," (this indicates the image data is indeed base64), render an `Image.memory` from the base64 data. -3. If the `src` starts with "asset:", render an `Image.asset` from the path in the `src`. -4. If the `src` ends with ".svg", render a `SvgPicture.network` (from the [`flutter_svg`](https://pub.dev/packages/flutter_svg) package) -5. Otherwise, just render an `Image.network`. - -If the rendering of any of the above fails, the package will fall back to rendering the alt text of the image, if any. - -Currently the package only considers the width, height, src, and alt text while rendering an image. +## FAQ -Note that there currently is no support for SVGs either in base64 format or asset format. +### Why can't I get `