Skip to content

Commit c21d2d9

Browse files
committed
Merge branch 'master' of https://github.com/Sub6Resources/flutter_html into feature/upgrade-custom-render
� Conflicts: � lib/flutter_html.dart � lib/html_parser.dart
2 parents ae79477 + 2a21971 commit c21d2d9

File tree

9 files changed

+130
-44
lines changed

9 files changed

+130
-44
lines changed

README.md

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

114114

115115
## Currently Supported CSS Attributes:
@@ -256,6 +256,8 @@ The `CustomRender` class has two constructors: `CustomRender.fromWidget()` and `
256256

257257
To use this API, create a matching function and an instance of `CustomRender`.
258258

259+
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.
260+
259261
#### Example Usages - customRender:
260262
1. Simple example - rendering custom HTML tags
261263

@@ -276,6 +278,7 @@ Widget html = Html(
276278
size: context.style.fontSize!.size! * 5,
277279
)),
278280
},
281+
tagsList: Html.tags..addAll(["bird", "flutter"]),
279282
);
280283
281284
CustomRenderMatcher birdMatcher() => (context) => context.tree.element?.localName == 'bird';

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: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ library flutter_html;
22

33
//export image render api
44
export 'package:flutter_html/image_render.dart';
5-
export 'package:flutter_html/custom_render.dart';
65
//export style api
76
export 'package:flutter_html/style.dart';
87
//export render context api
@@ -20,8 +19,33 @@ import 'package:flutter_html/html_parser.dart';
2019
import 'package:flutter_html/image_render.dart';
2120
import 'package:flutter_html/src/html_elements.dart';
2221
import 'package:flutter_html/style.dart';
23-
import 'package:webview_flutter/webview_flutter.dart';
2422
import 'package:html/dom.dart' as dom;
23+
import 'package:webview_flutter/webview_flutter.dart';
24+
25+
//export render context api
26+
export 'package:flutter_html/html_parser.dart';
27+
//export render context api
28+
export 'package:flutter_html/html_parser.dart';
29+
//export image render api
30+
export 'package:flutter_html/image_render.dart';
31+
//export image render api
32+
export 'package:flutter_html/image_render.dart';
33+
export 'package:flutter_html/src/anchor.dart';
34+
export 'package:flutter_html/src/anchor.dart';
35+
export 'package:flutter_html/src/interactable_element.dart';
36+
export 'package:flutter_html/src/interactable_element.dart';
37+
//export src for advanced custom render uses (e.g. casting context.tree)
38+
export 'package:flutter_html/src/layout_element.dart';
39+
//export src for advanced custom render uses (e.g. casting context.tree)
40+
export 'package:flutter_html/src/layout_element.dart';
41+
export 'package:flutter_html/src/replaced_element.dart';
42+
export 'package:flutter_html/src/replaced_element.dart';
43+
export 'package:flutter_html/src/styled_element.dart';
44+
export 'package:flutter_html/src/styled_element.dart';
45+
//export style api
46+
export 'package:flutter_html/style.dart';
47+
//export style api
48+
export 'package:flutter_html/style.dart';
2549

2650
class Html extends StatelessWidget {
2751
/// The `Html` widget takes HTML as input and displays a RichText
@@ -51,6 +75,7 @@ class Html extends StatelessWidget {
5175
/// See [its wiki page](https://github.com/Sub6Resources/flutter_html/wiki/Style) for more info.
5276
Html({
5377
Key? key,
78+
GlobalKey? anchorKey,
5479
required this.data,
5580
this.onLinkTap,
5681
this.customRenders = const {},
@@ -63,13 +88,14 @@ class Html extends StatelessWidget {
6388
this.tagsList = const [],
6489
this.style = const {},
6590
this.navigationDelegateForIframe,
66-
}) : document = null,
67-
assert (data != null),
68-
anchorKey = GlobalKey(),
91+
}) : document = null,
92+
assert(data != null),
93+
_anchorKey = anchorKey ?? GlobalKey(),
6994
super(key: key);
7095

7196
Html.fromDom({
7297
Key? key,
98+
GlobalKey? anchorKey,
7399
@required this.document,
74100
this.onLinkTap,
75101
this.customRenders = const {},
@@ -82,13 +108,13 @@ class Html extends StatelessWidget {
82108
this.tagsList = const [],
83109
this.style = const {},
84110
this.navigationDelegateForIframe,
85-
}) : data = null,
111+
}) : data = null,
86112
assert(document != null),
87-
anchorKey = GlobalKey(),
113+
_anchorKey = anchorKey ?? GlobalKey(),
88114
super(key: key);
89115

90116
/// A unique key for this Html widget to ensure uniqueness of anchors
91-
final Key anchorKey;
117+
final GlobalKey _anchorKey;
92118

93119
/// The HTML data passed to the widget as a String
94120
final String? data;
@@ -113,7 +139,6 @@ class Html extends StatelessWidget {
113139
/// You can return a widget here to override the default error widget.
114140
final OnMathError? onMathError;
115141

116-
117142
/// A parameter that should be set when the HTML widget is expected to be
118143
/// flexible
119144
final bool shrinkWrap;
@@ -145,13 +170,14 @@ class Html extends StatelessWidget {
145170

146171
@override
147172
Widget build(BuildContext context) {
148-
final dom.Document doc = data != null ? HtmlParser.parseHTML(data!) : document!;
173+
final dom.Document doc =
174+
data != null ? HtmlParser.parseHTML(data!) : document!;
149175
final double? width = shrinkWrap ? null : MediaQuery.of(context).size.width;
150176

151177
return Container(
152178
width: width,
153179
child: HtmlParser(
154-
key: anchorKey,
180+
key: _anchorKey,
155181
htmlData: doc,
156182
onLinkTap: onLinkTap,
157183
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';
@@ -712,7 +713,7 @@ class StyledText extends StatelessWidget {
712713
@override
713714
Widget build(BuildContext context) {
714715
return SizedBox(
715-
width: calculateWidth(style.display, renderContext),
716+
width: consumeExpandedBlock(style.display, renderContext),
716717
child: Text.rich(
717718
textSpan,
718719
style: style.generateTextStyle(),
@@ -725,13 +726,10 @@ class StyledText extends StatelessWidget {
725726
);
726727
}
727728

728-
double? calculateWidth(Display? display, RenderContext context) {
729+
double? consumeExpandedBlock(Display? display, RenderContext context) {
729730
if ((display == Display.BLOCK || display == Display.LIST_ITEM) && !renderContext.parser.shrinkWrap) {
730731
return double.infinity;
731732
}
732-
if (renderContext.parser.shrinkWrap) {
733-
return MediaQuery.of(context.buildContext).size.width;
734-
}
735733
return null;
736734
}
737735
}

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/replaced_element.dart

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,19 @@ class ImageContentElement extends ReplacedElement {
8383
for (final entry in context.parser.imageRenders.entries) {
8484
if (entry.key.call(attributes, element)) {
8585
final widget = entry.value.call(context, attributes, element);
86-
return RawGestureDetector(
87-
key: AnchorKey.of(context.parser.key, this),
88-
child: widget,
89-
gestures: {
90-
MultipleTapGestureRecognizer: GestureRecognizerFactoryWithHandlers<MultipleTapGestureRecognizer>(
91-
() => MultipleTapGestureRecognizer(), (instance) {
92-
instance..onTap = () => context.parser.onImageTap?.call(src, context, attributes, element);
86+
return Builder(
87+
builder: (buildContext) {
88+
return GestureDetector(
89+
key: AnchorKey.of(context.parser.key, this),
90+
child: widget,
91+
onTap: () {
92+
if (MultipleTapGestureDetector.of(buildContext) != null) {
93+
MultipleTapGestureDetector.of(buildContext)!.onTap?.call();
94+
}
95+
context.parser.onImageTap?.call(src, context, attributes, element);
9396
},
94-
),
95-
},
97+
);
98+
}
9699
);
97100
}
98101
}
@@ -283,13 +286,13 @@ class MathElement extends ReplacedElement {
283286
required this.element,
284287
this.texStr,
285288
String name = "math",
286-
}) : super(name: name, alignment: PlaceholderAlignment.middle, style: Style(), elementId: element.id);
289+
}) : super(name: name, alignment: PlaceholderAlignment.middle, style: Style(display: Display.BLOCK), elementId: element.id);
287290

288291
@override
289292
Widget toWidget(RenderContext context) {
290293
texStr = parseMathRecursive(element, r'');
291294
return Container(
292-
width: MediaQuery.of(context.buildContext).size.width,
295+
width: context.parser.shrinkWrap ? null : MediaQuery.of(context.buildContext).size.width,
293296
child: Math.tex(
294297
texStr ?? '',
295298
mathStyle: MathStyle.display,

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
@@ -48,11 +48,21 @@ class Context<T> {
4848

4949
// This class is a workaround so that both an image
5050
// and a link can detect taps at the same time.
51-
class MultipleTapGestureRecognizer extends TapGestureRecognizer {
52-
@override
53-
void rejectGesture(int pointer) {
54-
acceptGesture(pointer);
51+
class MultipleTapGestureDetector extends InheritedWidget {
52+
final void Function()? onTap;
53+
54+
const MultipleTapGestureDetector({
55+
Key? key,
56+
required Widget child,
57+
required this.onTap,
58+
}) : super(key: key, child: child);
59+
60+
static MultipleTapGestureDetector? of(BuildContext context) {
61+
return context.dependOnInheritedWidgetOfExactType<MultipleTapGestureDetector>();
5562
}
63+
64+
@override
65+
bool updateShouldNotify(MultipleTapGestureDetector oldWidget) => false;
5666
}
5767

5868
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)