Skip to content

Commit 6bdf8f9

Browse files
committed
Merge branch 'master' of https://github.com/Sub6Resources/flutter_html into feature/modularization
� Conflicts: � README.md � lib/flutter_html.dart � lib/html_parser.dart � lib/src/replaced_element.dart
2 parents 53d8c17 + 2a21971 commit 6bdf8f9

File tree

8 files changed

+117
-31
lines changed

8 files changed

+117
-31
lines changed

README.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,13 @@ Add the following to your `pubspec.yaml` file:
9393
|------------|-----------|-------|-------------|---------|---------|-------|------|--------|--------|--------|
9494
|`a` | `abbr` | `acronym`| `address` | `article`| `aside` | `audio`| `b` | `bdi` | `bdo` | `big` |
9595
|`blockquote`| `body` | `br` | `caption` | `cite` | `code` | `data`| `dd` | `del` | `details` | `dfn` |
96-
| `div` | `dl` | `dt` | `em` | `figcaption`| `figure`| `footer`| `h1` | `h2` | `h3` | `h4` |
97-
| `h5` |`h6` | `header` | `hr` | `i` | `iframe`| `img` | `ins` | `kbd`| `li` | `main` |
98-
| `mark` | `nav` | `noscript`|`ol` | `p` | `pre` | `q` | `rp` | `rt` | `ruby` | `s` |
99-
| `samp` | `section` | `small` | `span`| `strike` | `strong`| `sub` | `sup` | `summary` | `svg`| `table`|
100-
| `tbody` | `td` | `template` | `tfoot` | `th` | `thead` |`time` | `tr` | `tt` | `u` | `ul` |
101-
| `var` | `video` | `math`: | `mrow` | `msup` | `msub` | `mover` | `munder` | `msubsup` | `moverunder` | `mfrac` |
102-
| `mlongdiv` | `msqrt` | `mroot` | `mi` | `mn` | `mo` | | | | | |
96+
| `div` | `dl` | `dt` | `em` | `figcaption`| `figure`| `footer`| `font` | `h1` | `h2` | `h3` |
97+
| `h4` | `h5` |`h6` | `header` | `hr` | `i` | `iframe`| `img` | `ins` | `kbd`| `li` |
98+
| `main` | `mark` | `nav` | `noscript`|`ol` | `p` | `pre` | `q` | `rp` | `rt` | `ruby` |
99+
| `s` | `samp` | `section` | `small` | `span`| `strike` | `strong`| `sub` | `sup` | `summary` | `svg`|
100+
| `table` | `tbody` | `td` | `template` | `tfoot` | `th` | `thead` |`time` | `tr` | `tt` | `u` |
101+
| `ul` | `var` | `video` | `math`: | `mrow` | `msup` | `msub` | `mover` | `munder` | `msubsup` | `moverunder` |
102+
| `mfrac` | `mlongdiv` | `msqrt` | `mroot` | `mi` | `mn` | `mo` | | | | |
103103

104104

105105
## Currently Supported CSS Attributes:
@@ -244,6 +244,8 @@ The `CustomRender` class has two constructors: `CustomRender.fromWidget()` and `
244244
To use this API, create a matching function and an instance of `CustomRender`.
245245

246246
#### Example Usages - customRenders:
247+
Note: If you add any custom tags, you must add these tags to the [`tagsList`](#tagslist) parameter, otherwise they will not be rendered. See below for an example.
248+
247249
1. Simple example - rendering custom HTML tags
248250

249251
```dart
@@ -263,6 +265,7 @@ Widget html = Html(
263265
size: context.style.fontSize!.size! * 5,
264266
)),
265267
},
268+
tagsList: Html.tags..addAll(["bird", "flutter"]),
266269
);
267270
268271
CustomRenderMatcher birdMatcher() => (context) => context.tree.element?.localName == 'bird';
@@ -299,6 +302,8 @@ Widget html = Html(
299302
CustomRenderMatcher tableMatcher() => (context) => context.tree.element?.localName == "table";
300303
```
301304

305+
</details>
306+
302307
3. Complex example - rendering an `iframe` differently based on whether it is an embedded youtube video or some other embedded content.
303308

304309
<details><summary>View code</summary>

example/android/app/build.gradle

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,8 @@ android {
3232
}
3333

3434
defaultConfig {
35-
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
3635
applicationId "com.example.example"
37-
minSdkVersion 16
36+
minSdkVersion 19
3837
targetSdkVersion 28
3938
versionCode flutterVersionCode.toInteger()
4039
versionName flutterVersionName
@@ -43,8 +42,6 @@ android {
4342

4443
buildTypes {
4544
release {
46-
// TODO: Add your own signing config for the release build.
47-
// Signing with the debug keys for now, so `flutter run --release` works.
4845
signingConfig signingConfigs.debug
4946
}
5047
}

lib/flutter_html.dart

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
library flutter_html;
22

3-
//export custom render api
4-
export 'package:flutter_html/custom_render.dart';
3+
//export image render api
4+
export 'package:flutter_html/image_render.dart';
55
//export style api
66
export 'package:flutter_html/style.dart';
77
//export render context api
@@ -11,8 +11,6 @@ export 'package:flutter_html/src/layout_element.dart';
1111
export 'package:flutter_html/src/replaced_element.dart';
1212
export 'package:flutter_html/src/styled_element.dart';
1313
export 'package:flutter_html/src/interactable_element.dart';
14-
//export anchor for use in flutter_html_<tag> packages
15-
export 'package:flutter_html/src/anchor.dart';
1614

1715
import 'package:flutter/material.dart';
1816
import 'package:flutter_html/custom_render.dart';
@@ -21,6 +19,32 @@ import 'package:flutter_html/html_parser.dart';
2119
import 'package:flutter_html/src/html_elements.dart';
2220
import 'package:flutter_html/style.dart';
2321
import 'package:html/dom.dart' as dom;
22+
import 'package:webview_flutter/webview_flutter.dart';
23+
24+
//export render context api
25+
export 'package:flutter_html/html_parser.dart';
26+
//export render context api
27+
export 'package:flutter_html/html_parser.dart';
28+
//export image render api
29+
export 'package:flutter_html/image_render.dart';
30+
//export image render api
31+
export 'package:flutter_html/image_render.dart';
32+
export 'package:flutter_html/src/anchor.dart';
33+
export 'package:flutter_html/src/anchor.dart';
34+
export 'package:flutter_html/src/interactable_element.dart';
35+
export 'package:flutter_html/src/interactable_element.dart';
36+
//export src for advanced custom render uses (e.g. casting context.tree)
37+
export 'package:flutter_html/src/layout_element.dart';
38+
//export src for advanced custom render uses (e.g. casting context.tree)
39+
export 'package:flutter_html/src/layout_element.dart';
40+
export 'package:flutter_html/src/replaced_element.dart';
41+
export 'package:flutter_html/src/replaced_element.dart';
42+
export 'package:flutter_html/src/styled_element.dart';
43+
export 'package:flutter_html/src/styled_element.dart';
44+
//export style api
45+
export 'package:flutter_html/style.dart';
46+
//export style api
47+
export 'package:flutter_html/style.dart';
2448

2549
class Html extends StatelessWidget {
2650
/// The `Html` widget takes HTML as input and displays a RichText
@@ -50,6 +74,7 @@ class Html extends StatelessWidget {
5074
/// See [its wiki page](https://github.com/Sub6Resources/flutter_html/wiki/Style) for more info.
5175
Html({
5276
Key? key,
77+
GlobalKey? anchorKey,
5378
required this.data,
5479
this.onLinkTap,
5580
this.customRenders = const {},
@@ -61,11 +86,12 @@ class Html extends StatelessWidget {
6186
this.style = const {},
6287
}) : document = null,
6388
assert (data != null),
64-
anchorKey = GlobalKey(),
89+
_anchorKey = anchorKey ?? GlobalKey(),
6590
super(key: key);
6691

6792
Html.fromDom({
6893
Key? key,
94+
GlobalKey? anchorKey,
6995
@required this.document,
7096
this.onLinkTap,
7197
this.customRenders = const {},
@@ -77,11 +103,11 @@ class Html extends StatelessWidget {
77103
this.style = const {},
78104
}) : data = null,
79105
assert(document != null),
80-
anchorKey = GlobalKey(),
106+
_anchorKey = anchorKey ?? GlobalKey(),
81107
super(key: key);
82108

83109
/// A unique key for this Html widget to ensure uniqueness of anchors
84-
final Key anchorKey;
110+
final GlobalKey _anchorKey;
85111

86112
/// The HTML data passed to the widget as a String
87113
final String? data;
@@ -125,13 +151,14 @@ class Html extends StatelessWidget {
125151

126152
@override
127153
Widget build(BuildContext context) {
128-
final dom.Document doc = data != null ? HtmlParser.parseHTML(data!) : document!;
154+
final dom.Document doc =
155+
data != null ? HtmlParser.parseHTML(data!) : document!;
129156
final double? width = shrinkWrap ? null : MediaQuery.of(context).size.width;
130157

131158
return Container(
132159
width: width,
133160
child: HtmlParser(
134-
key: anchorKey,
161+
key: _anchorKey,
135162
htmlData: doc,
136163
onLinkTap: onLinkTap,
137164
onImageTap: onImageTap,

lib/html_parser.dart

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:collection';
22
import 'dart:math';
33

4+
import 'package:collection/collection.dart';
45
import 'package:csslib/parser.dart' as cssparser;
56
import 'package:csslib/visitor.dart' as css;
67
import 'package:flutter/material.dart';
@@ -690,7 +691,7 @@ class StyledText extends StatelessWidget {
690691
@override
691692
Widget build(BuildContext context) {
692693
return SizedBox(
693-
width: calculateWidth(style.display, renderContext),
694+
width: consumeExpandedBlock(style.display, renderContext),
694695
child: Text.rich(
695696
textSpan,
696697
style: style.generateTextStyle(),
@@ -703,13 +704,10 @@ class StyledText extends StatelessWidget {
703704
);
704705
}
705706

706-
double? calculateWidth(Display? display, RenderContext context) {
707+
double? consumeExpandedBlock(Display? display, RenderContext context) {
707708
if ((display == Display.BLOCK || display == Display.LIST_ITEM) && !renderContext.parser.shrinkWrap) {
708709
return double.infinity;
709710
}
710-
if (renderContext.parser.shrinkWrap) {
711-
return MediaQuery.of(context.buildContext).size.width;
712-
}
713711
return null;
714712
}
715713
}

lib/src/anchor.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,28 @@ import 'package:flutter/widgets.dart';
33
import 'package:flutter_html/src/styled_element.dart';
44

55
class AnchorKey extends GlobalKey {
6+
static final Set<AnchorKey> _registry = <AnchorKey>{};
7+
68
final Key parentKey;
79
final String id;
810

911
const AnchorKey._(this.parentKey, this.id) : super.constructor();
1012

1113
static AnchorKey? of(Key? parentKey, StyledElement? id) {
12-
return forId(parentKey, id?.elementId);
14+
final key = forId(parentKey, id?.elementId);
15+
if (key == null || _registry.contains(key)) {
16+
// Invalid id or already created a key with this id: silently ignore
17+
return null;
18+
}
19+
_registry.add(key);
20+
return key;
1321
}
1422

1523
static AnchorKey? forId(Key? parentKey, String? id) {
1624
if (parentKey == null || id == null || id.isEmpty || id == "[[No ID]]") {
1725
return null;
1826
}
27+
1928
return AnchorKey._(parentKey, id);
2029
}
2130

lib/src/styled_element.dart

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:flutter/material.dart';
2+
import 'package:flutter_html/src/css_parser.dart';
23
import 'package:flutter_html/style.dart';
34
import 'package:html/dom.dart' as dom;
45
//TODO(Sub6Resources): don't use the internal code of the html package as it may change unexpectedly.
@@ -179,6 +180,17 @@ StyledElement parseStyledElement(
179180
display: Display.BLOCK,
180181
);
181182
break;
183+
case "font":
184+
styledElement.style = Style(
185+
color: element.attributes['color'] != null ?
186+
element.attributes['color']!.startsWith("#") ?
187+
ExpressionMapping.stringToColor(element.attributes['color']!) :
188+
ExpressionMapping.namedColorToColor(element.attributes['color']!) :
189+
null,
190+
fontFamily: element.attributes['face']?.split(",").first,
191+
fontSize: numberToFontSize(element.attributes['size'] ?? ''),
192+
);
193+
break;
182194
case "h1":
183195
styledElement.style = Style(
184196
fontSize: FontSize.xxLarge,
@@ -368,3 +380,31 @@ StyledElement parseStyledElement(
368380
}
369381

370382
typedef ListCharacter = String Function(int i);
383+
384+
FontSize numberToFontSize(String num) {
385+
switch (num) {
386+
case "1":
387+
return FontSize.xxSmall;
388+
case "2":
389+
return FontSize.xSmall;
390+
case "3":
391+
return FontSize.small;
392+
case "4":
393+
return FontSize.medium;
394+
case "5":
395+
return FontSize.large;
396+
case "6":
397+
return FontSize.xLarge;
398+
case "7":
399+
return FontSize.xxLarge;
400+
}
401+
if (num.startsWith("+")) {
402+
final relativeNum = double.tryParse(num.substring(1)) ?? 0;
403+
return numberToFontSize((3 + relativeNum).toString());
404+
}
405+
if (num.startsWith("-")) {
406+
final relativeNum = double.tryParse(num.substring(1)) ?? 0;
407+
return numberToFontSize((3 - relativeNum).toString());
408+
}
409+
return FontSize.medium;
410+
}

lib/src/utils.dart

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,21 @@ class Context<T> {
2828

2929
// This class is a workaround so that both an image
3030
// and a link can detect taps at the same time.
31-
class MultipleTapGestureRecognizer extends TapGestureRecognizer {
32-
@override
33-
void rejectGesture(int pointer) {
34-
acceptGesture(pointer);
31+
class MultipleTapGestureDetector extends InheritedWidget {
32+
final void Function()? onTap;
33+
34+
const MultipleTapGestureDetector({
35+
Key? key,
36+
required Widget child,
37+
required this.onTap,
38+
}) : super(key: key, child: child);
39+
40+
static MultipleTapGestureDetector? of(BuildContext context) {
41+
return context.dependOnInheritedWidgetOfExactType<MultipleTapGestureDetector>();
3542
}
43+
44+
@override
45+
bool updateShouldNotify(MultipleTapGestureDetector oldWidget) => false;
3646
}
3747

3848
class CustomBorderSide {

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: flutter_html
22
description: A Flutter widget rendering static HTML and CSS as Flutter widgets.
3-
version: 2.0.0-nullsafety.1
3+
version: 2.0.0
44
homepage: https://github.com/Sub6Resources/flutter_html
55

66
environment:

0 commit comments

Comments
 (0)