Skip to content

Commit ea949e8

Browse files
committed
Merge remote-tracking branch 'upstream/master' into feature/268-inner-links
# Conflicts: # example/lib/main.dart # lib/flutter_html.dart # lib/html_parser.dart # lib/src/layout_element.dart
2 parents 9ec83cb + df53f43 commit ea949e8

14 files changed

+753
-80
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,5 @@ modules.xml
149149
**/.flutter-plugins-dependencies
150150

151151
**/flutter_export_environment.sh
152+
153+
/example/ios/Flutter/Flutter.podspec

README.md

Lines changed: 116 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,27 @@ A Flutter widget for rendering HTML and CSS as Flutter widgets.
2727

2828
- [Currently Supported CSS Attributes](#currently-supported-css-attributes)
2929

30+
- [Currently Supported Inline CSS Attributes](#currently-supported-inline-css-attributes)
31+
3032
- [Why flutter_html?](#why-this-package)
3133

3234
- [API Reference](#api-reference)
3335

36+
- [Constructors](#constructors)
37+
3438
- [Parameters Table](#parameters)
3539

3640
- [Data](#data)
41+
42+
- [Document](#document)
3743

3844
- [onLinkTap](#onlinktap)
3945

4046
- [customRender](#customrender)
4147

4248
- [onImageError](#onimageerror)
49+
50+
- [onMathError](#onmatherror)
4351

4452
- [onImageTap](#onimagetap)
4553

@@ -69,6 +77,10 @@ A Flutter widget for rendering HTML and CSS as Flutter widgets.
6977

7078
- [SVG](#svg)
7179

80+
- [MathML](#mathml)
81+
82+
- [Tex](#tex)
83+
7284
- [Table](#table)
7385

7486
- [Notes](#notes)
@@ -94,8 +106,8 @@ Add the following to your `pubspec.yaml` file:
94106
| `mark` | `nav` | `noscript`|`ol` | `p` | `pre` | `q` | `rp` | `rt` | `ruby` | `s` |
95107
| `samp` | `section` | `small` | `span`| `strike` | `strong`| `sub` | `sup` | `summary` | `svg`| `table`|
96108
| `tbody` | `td` | `template` | `tfoot` | `th` | `thead` |`time` | `tr` | `tt` | `u` | `ul` |
97-
| `var` | `video` | | | | | | | | | |
98-
109+
| `var` | `video` | `math`: | `mrow` | `msup` | `msub` | `mover` | `munder` | `msubsup` | `moverunder` | `mfrac` |
110+
| `mlongdiv` | `msqrt` | `mroot` | `mi` | `mn` | `mo` | | | | | |
99111

100112

101113
## Currently Supported CSS Attributes:
@@ -106,6 +118,13 @@ Add the following to your `pubspec.yaml` file:
106118
|`padding` | `margin`| `text-align`| `text-decoration`| `text-decoration-color`| `text-decoration-style`| `text-decoration-thickness`|
107119
|`text-shadow` | `vertical-align`| `white-space`| `width` | `word-spacing`| | |
108120

121+
## Currently Supported Inline CSS Attributes:
122+
| | | | | | | |
123+
|------------------|--------|------------|----------|--------------|------------------------|------------|
124+
|`background-color`| `border` | `color`| `direction`| `display`| `font-family`| `font-feature-settings` |
125+
| `font-size`|`font-style` | `font-weight`| `line-height` | `list-style-type` | `list-style-position`|`padding` |
126+
| `margin`| `text-align`| `text-decoration`| `text-decoration-color`| `text-decoration-style`| `text-shadow` | |
127+
109128
Don't see a tag or attribute you need? File a feature request or contribute to the project!
110129

111130
## Why this package?
@@ -122,14 +141,24 @@ For a full example, see [here](https://github.com/Sub6Resources/flutter_html/tre
122141

123142
Below, you will find brief descriptions of the parameters the`Html` widget accepts and some code snippets to help you use this package.
124143

144+
## Constructors:
145+
146+
The package currently has two different constructors - `Html()` and `Html.fromDom()`.
147+
148+
The `Html()` constructor is for those who would like to directly pass HTML from the source to the package to be rendered.
149+
150+
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.
151+
125152
### Parameters:
126153

127154
| Parameters | Description |
128155
|--------------|-----------------|
129-
| `data` | The HTML data passed to the `Html` widget. This is required and cannot be null. |
156+
| `data` | The HTML data passed to the `Html` widget. This is required and cannot be null when using `Html()`. |
157+
| `document` | The DOM document passed to the `Html` widget. This is required and cannot be null when using `Html.fromDom()`. |
130158
| `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. |
131159
| `customRender` | A powerful API that allows you to customize everything when rendering a specific HTML tag. |
132160
| `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. |
161+
| `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`. |
133162
| `shrinkWrap` | A `bool` used while rendering different widgets to specify whether they should be shrink-wrapped or not, like `ContainerSpan` |
134163
| `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. |
135164
| `blacklistedElements` | A list of elements the `Html` widget should not render. The list should contain the tags of the HTML elements you wish to blacklist. |
@@ -139,7 +168,7 @@ Below, you will find brief descriptions of the parameters the`Html` widget accep
139168

140169
### Data:
141170

142-
The HTML data passed to the `Html` widget as a `String`. This is required and cannot be null.
171+
The HTML data passed to the `Html` widget as a `String`. This is required and cannot be null when using `Html`.
143172
Any HTML tags in the `String` that are not supported by the package will not be rendered.
144173

145174
#### Example Usage - Data:
@@ -160,6 +189,36 @@ Widget html = Html(
160189
);
161190
```
162191

192+
### Document:
193+
194+
The DOM document passed to the `Html` widget as a `Document`. This is required and cannot be null when using `Html.fromDom()`.
195+
Any HTML tags in the document that are not supported by the package will not be rendered.
196+
Using the `Html.fromDom()` constructor can be useful when you would like to sanitize the HTML string yourself before passing it to the package.
197+
198+
#### Example Usage - Document:
199+
200+
```dart
201+
import 'package:html/parser.dart' as htmlparser;
202+
import 'package:html/dom.dart' as dom;
203+
...
204+
String htmlData = """<div>
205+
<h1>Demo Page</h1>
206+
<p>This is a fantastic product that you should buy!</p>
207+
<h3>Features</h3>
208+
<ul>
209+
<li>It actually works</li>
210+
<li>It exists</li>
211+
<li>It doesn't cost much!</li>
212+
</ul>
213+
<!--You can pretty much put any html in here!-->
214+
</div>""";
215+
dom.Document document = htmlparser.parse(htmlData);
216+
/// sanitize or query document here
217+
Widget html = Html(
218+
document: document,
219+
);
220+
```
221+
163222
### onLinkTap:
164223

165224
A function that defines what the widget should do when a link is tapped.
@@ -283,6 +342,24 @@ Widget html = Html(
283342
);
284343
```
285344

345+
### onMathError:
346+
347+
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`.
348+
349+
#### Example Usage - onMathError:
350+
351+
```dart
352+
Widget html = Html(
353+
data: """<!-- Some MathML string that fails to parse -->""",
354+
onMathError: (String parsedTex, String error, String errorWithType) {
355+
//your logic here. A Widget must be returned from this function:
356+
return Text(error);
357+
//you can also try and fix the parsing yourself:
358+
return Math.tex(correctedParsedTex);
359+
},
360+
);
361+
```
362+
286363
### onImageTap:
287364

288365
A function that defines what the widget should do when an image is tapped.
@@ -627,6 +704,41 @@ This package renders svg elements using the [`flutter_svg`](https://pub.dev/pack
627704

628705
When rendering SVGs, the package takes the SVG data within the `<svg>` tag and passes it to `flutter_svg`. The `width` and `height` attributes are considered while rendering, if given.
629706

707+
### MathML
708+
709+
This package renders MathML elements using the [`flutter_math`](https://pub.dev/packages/flutter_math) plugin.
710+
711+
When rendering MathML, the package takes the MathML data within the `<math>` tag and tries to parse it to Tex. Then, it will pass the parsed string to `flutter_math`.
712+
713+
Because this package is parsing MathML to Tex, it may not support some functionalities. The current list of supported tags can be found [above](#currently-supported-html-tags), but some of these only have partial support at the moment.
714+
715+
If the parsing errors, you can use the [onMathError](#onmatherror) API to catch the error and potentially fix it on your end - you can analyze the error and the parsed string, and finally return a new instance of `Math.tex()` with the corrected Tex string.
716+
717+
If you'd like to see more MathML features, feel free to create a PR or file a feature request!
718+
719+
### Tex
720+
721+
If you have a Tex string you'd like to render inside your HTML you can do that using the same [`flutter_math`](https://pub.dev/packages/flutter_math) plugin.
722+
723+
Use a custom tag inside your HTML (an example could be `<tex>`), and place your **raw** Tex string inside.
724+
725+
Then, use the `customRender` parameter to add the widget to render Tex. It could look like this:
726+
727+
```dart
728+
Widget htmlWidget = Html(
729+
data: r"""<tex>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)</tex>""",
730+
customRender: {
731+
"tex": (_, __, ___, element) => Math.tex(
732+
element.text,
733+
onErrorFallback: (FlutterMathException e) {
734+
//return your error widget here e.g.
735+
return Text(e.message);
736+
},
737+
),
738+
}
739+
);
740+
```
741+
630742
### Table
631743

632744
This package renders table elements using the [`flutter_layout_grid`](https://pub.dev/packages/flutter_layout_grid) plugin.

example/lib/main.dart

Lines changed: 132 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_html/flutter_html.dart';
33
import 'package:flutter_html/image_render.dart';
4+
import 'package:flutter_html/src/layout_element.dart';
5+
import 'package:flutter_html/style.dart';
46

57
void main() => runApp(new MyApp());
68

@@ -27,15 +29,15 @@ class MyHomePage extends StatefulWidget {
2729
_MyHomePageState createState() => new _MyHomePageState();
2830
}
2931

30-
const htmlData = """
32+
const htmlData = r"""
3133
<p id='top'><a href='#bottom'>Scroll to bottom</a></p>
32-
<h1>Header 1</h1>
33-
<h2>Header 2</h2>
34-
<h3>Header 3</h3>
35-
<h4>Header 4</h4>
36-
<h5>Header 5</h5>
37-
<h6>Header 6</h6>
38-
<h3>Ruby Support:</h3>
34+
<h1>Header 1</h1>
35+
<h2>Header 2</h2>
36+
<h3>Header 3</h3>
37+
<h4>Header 4</h4>
38+
<h5>Header 5</h5>
39+
<h6>Header 6</h6>
40+
<h3>Ruby Support:</h3>
3941
<p>
4042
<ruby>
4143
漢<rt>かん</rt>
@@ -126,8 +128,10 @@ const htmlData = """
126128
<img src='asset:assets/html5.png' width='100' />
127129
<h3>Local asset svg</h3>
128130
<img src='asset:assets/mac.svg' width='100' />
129-
<h3>Base64</h3>
130-
<img alt='Red dot' src='data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==' />
131+
<h3>Data uri (with base64 support)</h3>
132+
<img alt='Red dot (png)' src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==' />
133+
<img alt='Green dot (base64 svg)' src='data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB2aWV3Qm94PSIwIDAgMzAgMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxjaXJjbGUgY3g9IjE1IiBjeT0iMTAiIHI9IjEwIiBmaWxsPSJncmVlbiIvPgo8L3N2Zz4=' />
134+
<img alt='Green dot (plain svg)' src='data:image/svg+xml,%3C?xml version="1.0" encoding="UTF-8"?%3E%3Csvg viewBox="0 0 30 20" xmlns="http://www.w3.org/2000/svg"%3E%3Ccircle cx="15" cy="10" r="10" fill="yellow"/%3E%3C/svg%3E' />
131135
<h3>Custom source matcher (relative paths)</h3>
132136
<img src='/wikipedia/commons/thumb/e/ef/Octicons-logo-github.svg/200px-Octicons-logo-github.svg.png' />
133137
<h3>Custom image render (flutter.dev)</h3>
@@ -137,6 +141,100 @@ const htmlData = """
137141
<img alt='Empty source' src='' />
138142
<h3>Broken network image</h3>
139143
<img alt='Broken image' src='https://www.notgoogle.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png' />
144+
<h3>MathML Support:</h3>
145+
<math>
146+
<mrow>
147+
<mi>x</mi>
148+
<mo>=</mo>
149+
<mfrac>
150+
<mrow>
151+
<mrow>
152+
<mo>-</mo>
153+
<mi>b</mi>
154+
</mrow>
155+
<mo>&PlusMinus;</mo>
156+
<msqrt>
157+
<mrow>
158+
<msup>
159+
<mi>b</mi>
160+
<mn>2</mn>
161+
</msup>
162+
<mo>-</mo>
163+
<mrow>
164+
<mn>4</mn>
165+
<mo>&InvisibleTimes;</mo>
166+
<mi>a</mi>
167+
<mo>&InvisibleTimes;</mo>
168+
<mi>c</mi>
169+
</mrow>
170+
</mrow>
171+
</msqrt>
172+
</mrow>
173+
<mrow>
174+
<mn>2</mn>
175+
<mo>&InvisibleTimes;</mo>
176+
<mi>a</mi>
177+
</mrow>
178+
</mfrac>
179+
</mrow>
180+
</math>
181+
<math>
182+
<munderover >
183+
<mo> &int; </mo>
184+
<mn> 0 </mn>
185+
<mi> 5 </mi>
186+
</munderover>
187+
<msup>
188+
<mi>x</mi>
189+
<mn>2</mn>
190+
</msup>
191+
<mo>&sdot;</mo>
192+
<mi>&dd;</mi><mi>x</mi>
193+
<mo>=</mo>
194+
<mo>[</mo>
195+
<mfrac>
196+
<mn>1</mn>
197+
<mi>3</mi>
198+
</mfrac>
199+
<msup>
200+
<mi>x</mi>
201+
<mn>3</mn>
202+
</msup>
203+
<msubsup>
204+
<mo>]</mo>
205+
<mn>0</mn>
206+
<mn>5</mn>
207+
</msubsup>
208+
<mo>=</mo>
209+
<mfrac>
210+
<mn>125</mn>
211+
<mi>3</mi>
212+
</mfrac>
213+
<mo>-</mo>
214+
<mn>0</mn>
215+
<mo>=</mo>
216+
<mfrac>
217+
<mn>125</mn>
218+
<mi>3</mi>
219+
</mfrac>
220+
</math>
221+
<math>
222+
<msup>
223+
<mo>sin</mo>
224+
<mn>2</mn>
225+
</msup>
226+
<mo>&theta;</mo>
227+
<mo>+</mo>
228+
<msup>
229+
<mo>cos</mo>
230+
<mn>2</mn>
231+
</msup>
232+
<mo>&theta;</mo>
233+
<mo>=</mo>
234+
<mn>1</mn>
235+
</math>
236+
<h3>Tex Support with the custom tex tag:</h3>
237+
<tex>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)</tex>
140238
<p id='bottom'><a href='#top'>Scroll to top</a></p>
141239
""";
142240

@@ -151,7 +249,30 @@ class _MyHomePageState extends State<MyHomePage> {
151249
body: SingleChildScrollView(
152250
child: Html(
153251
data: htmlData,
154-
//Optional parameters:
252+
style: {
253+
"table": Style(
254+
backgroundColor: Color.fromARGB(0x50, 0xee, 0xee, 0xee),
255+
),
256+
"tr": Style(
257+
border: Border(bottom: BorderSide(color: Colors.grey)),
258+
),
259+
"th": Style(
260+
padding: EdgeInsets.all(6),
261+
backgroundColor: Colors.grey,
262+
),
263+
"td": Style(
264+
padding: EdgeInsets.all(6),
265+
alignment: Alignment.topLeft,
266+
),
267+
},
268+
customRender: {
269+
"table": (context, child) {
270+
return SingleChildScrollView(
271+
scrollDirection: Axis.horizontal,
272+
child: (context.tree as TableLayoutElement).toWidget(context),
273+
);
274+
}
275+
},
155276
customImageRenders: {
156277
networkSourceMatcher(domains: ["flutter.dev"]): (context, attributes, element) {
157278
return FlutterLogo(size: 36);

0 commit comments

Comments
 (0)