From c68c59d8b1ba4827c00dea03497d95b5faa722f5 Mon Sep 17 00:00:00 2001
From: Matthew Whitaker
Date: Sat, 8 Feb 2020 14:02:57 -0700
Subject: [PATCH] Add additional CSS styles
---
example/ios/Flutter/Flutter.podspec | 18 +++++
example/lib/main.dart | 2 +-
lib/html_parser.dart | 53 ++++++++++---
lib/src/layout_element.dart | 5 +-
lib/src/styled_element.dart | 4 +-
lib/style.dart | 118 ++++++++++++++++++++++++++--
test/html_parser_test.dart | 4 +-
7 files changed, 176 insertions(+), 28 deletions(-)
create mode 100644 example/ios/Flutter/Flutter.podspec
diff --git a/example/ios/Flutter/Flutter.podspec b/example/ios/Flutter/Flutter.podspec
new file mode 100644
index 0000000000..5ca30416ba
--- /dev/null
+++ b/example/ios/Flutter/Flutter.podspec
@@ -0,0 +1,18 @@
+#
+# NOTE: This podspec is NOT to be published. It is only used as a local source!
+#
+
+Pod::Spec.new do |s|
+ s.name = 'Flutter'
+ s.version = '1.0.0'
+ s.summary = 'High-performance, high-fidelity mobile apps.'
+ s.description = <<-DESC
+Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS.
+ DESC
+ s.homepage = 'https://flutter.io'
+ s.license = { :type => 'MIT' }
+ s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
+ s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s }
+ s.ios.deployment_target = '8.0'
+ s.vendored_frameworks = 'Flutter.framework'
+end
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 17023a4f65..b8f958f93d 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -46,7 +46,7 @@ const htmlData = """
Support for sub
/sup
Solve for xn: log2(x2+n) = 93
- One of the most common equations in all of physics is E=mc2.
+ One of the most common equations in all of physics is E=mc2.
Table support:
diff --git a/lib/html_parser.dart b/lib/html_parser.dart
index 15f59a421e..c8a6e87876 100644
--- a/lib/html_parser.dart
+++ b/lib/html_parser.dart
@@ -1,16 +1,16 @@
import 'dart:collection';
import 'dart:math';
+import 'package:csslib/parser.dart' as cssparser;
+import 'package:csslib/visitor.dart' as css;
+import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
+import 'package:flutter_html/src/html_elements.dart';
import 'package:flutter_html/src/layout_element.dart';
import 'package:flutter_html/src/utils.dart';
import 'package:flutter_html/style.dart';
-import 'package:flutter/material.dart';
-import 'package:csslib/visitor.dart' as css;
import 'package:html/dom.dart' as dom;
-import 'package:flutter_html/src/html_elements.dart';
import 'package:html/parser.dart' as htmlparser;
-import 'package:csslib/parser.dart' as cssparser;
typedef OnTap = void Function(String url);
typedef CustomRender = Widget Function(
@@ -67,7 +67,7 @@ class HtmlParser extends StatelessWidget {
cleanedTree,
);
- return RichText(text: parsedTree);
+ return StyledText(textSpan: parsedTree, style: cleanedTree.style);
}
/// [parseHTML] converts a string of HTML to a DOM document using the dart `html` library.
@@ -281,14 +281,15 @@ class HtmlParser extends StatelessWidget {
Padding(
padding: EdgeInsets.only(
left: 30), //TODO derive this from list padding.
- child: RichText(
- text: TextSpan(
+ child: StyledText(
+ textSpan: TextSpan(
children: tree.children
?.map((tree) => parseTree(newContext, tree))
?.toList() ??
[],
style: newContext.style.generateTextStyle(),
),
+ style: newContext.style,
),
)
],
@@ -317,14 +318,15 @@ class HtmlParser extends StatelessWidget {
},
),
},
- child: RichText(
- text: TextSpan(
+ child: StyledText(
+ textSpan: TextSpan(
style: newContext.style.generateTextStyle(),
children: tree.children
.map((tree) => parseTree(newContext, tree))
.toList() ??
[],
),
+ style: newContext.style,
),
),
);
@@ -349,14 +351,15 @@ class HtmlParser extends StatelessWidget {
return WidgetSpan(
child: Transform.translate(
offset: Offset(0, verticalOffset),
- child: RichText(
- text: TextSpan(
+ child: StyledText(
+ textSpan: TextSpan(
style: newContext.style.generateTextStyle(),
children: tree.children
.map((tree) => parseTree(newContext, tree))
.toList() ??
[],
),
+ style: newContext.style,
),
),
);
@@ -690,12 +693,36 @@ class ContainerSpan extends StatelessWidget {
margin: style?.margin,
alignment: shrinkWrap ? null : style?.alignment,
child: child ??
- RichText(
- text: TextSpan(
+ StyledText(
+ textSpan: TextSpan(
style: newContext.style.generateTextStyle(),
children: children,
),
+ style: newContext.style,
),
);
}
}
+
+class StyledText extends StatelessWidget {
+ final InlineSpan textSpan;
+ final Style style;
+
+ const StyledText({
+ this.textSpan,
+ this.style,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ width: style.display == Display.BLOCK || style.display == Display.LIST_ITEM? double.infinity: null,
+ child: Text.rich(
+ textSpan,
+ style: style.generateTextStyle(),
+ textAlign: style.textAlign,
+ textDirection: style.direction,
+ ),
+ );
+ }
+}
diff --git a/lib/src/layout_element.dart b/lib/src/layout_element.dart
index 82bc202350..d3c652f929 100644
--- a/lib/src/layout_element.dart
+++ b/lib/src/layout_element.dart
@@ -125,8 +125,9 @@ class TableRowLayoutElement extends LayoutElement {
color: c.style.backgroundColor,
border: c.style.border,
),
- child: RichText(
- text: context.parser.parseTree(context, c),
+ child: StyledText(
+ textSpan: context.parser.parseTree(context, c),
+ style: c.style,
)));
}
return null;
diff --git a/lib/src/styled_element.dart b/lib/src/styled_element.dart
index 8c44da4370..aaa0dc90e9 100644
--- a/lib/src/styled_element.dart
+++ b/lib/src/styled_element.dart
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_html/style.dart';
import 'package:html/dom.dart' as dom;
-//TODO(Sub6Resources) don't use the internal code of the html package as it may change unexpectedly.
+//TODO(Sub6Resources): don't use the internal code of the html package as it may change unexpectedly.
import 'package:html/src/query_selector.dart';
/// A [StyledElement] applies a style to all of its children.
@@ -85,7 +85,7 @@ StyledElement parseStyledElement(
? TextDirection.rtl
: TextDirection.ltr;
styledElement.style = Style(
- textDirection: textDirection,
+ direction: textDirection,
);
break;
case "big":
diff --git a/lib/style.dart b/lib/style.dart
index d6a43be824..616228b2d9 100644
--- a/lib/style.dart
+++ b/lib/style.dart
@@ -1,3 +1,5 @@
+import 'dart:ui';
+
import 'package:flutter/material.dart';
///This class represents all the available CSS attributes
@@ -15,6 +17,12 @@ class Style {
/// Default: unspecified,
Color color;
+ /// CSS attribute "`direction`"
+ ///
+ /// Inherited: yes,
+ /// Default: TextDirection.ltr,
+ TextDirection direction;
+
/// CSS attribute "`display`"
///
/// Inherited: no,
@@ -27,6 +35,12 @@ class Style {
/// Default: Theme.of(context).style.textTheme.body1.fontFamily
String fontFamily;
+ /// CSS attribute "`font-feature-settings`"
+ ///
+ /// Inherited: yes,
+ /// Default: normal
+ List fontFeatureSettings;
+
/// CSS attribute "`font-size`"
///
/// Inherited: yes,
@@ -51,6 +65,12 @@ class Style {
/// Default: Unspecified (null),
double height;
+ /// CSS attribute "`letter-spacing`"
+ ///
+ /// Inherited: yes,
+ /// Default: normal (0),
+ double letterSpacing;
+
/// CSS attribute "`list-style-type`"
///
/// Inherited: yes,
@@ -69,18 +89,45 @@ class Style {
/// Default: EdgeInsets.zero
EdgeInsets margin;
+ /// CSS attribute "`text-align`"
+ ///
+ /// Inherited: yes,
+ /// Default: TextAlign.start,
+ TextAlign textAlign;
+
/// CSS attribute "`text-decoration`"
///
/// Inherited: no,
/// Default: TextDecoration.none,
TextDecoration textDecoration;
+ /// CSS attribute "`text-decoration-color`"
+ ///
+ /// Inherited: no,
+ /// Default: Current color
+ Color textDecorationColor;
+
/// CSS attribute "`text-decoration-style`"
///
/// Inherited: no,
/// Default: TextDecorationStyle.solid,
TextDecorationStyle textDecorationStyle;
+ /// Loosely based on CSS attribute "`text-decoration-thickness`"
+ ///
+ /// Uses a percent modifier based on the font size.
+ ///
+ /// Inherited: no,
+ /// Default: 1.0 (specified by font size)
+ // TODO(Sub6Resources): Possibly base this more closely on the CSS attribute.
+ double textDecorationThickness;
+
+ /// CSS attribute "`text-shadow`"
+ ///
+ /// Inherited: yes,
+ /// Default: none,
+ List textShadow;
+
/// CSS attribute "`vertical-align`"
///
/// Inherited: no,
@@ -99,10 +146,15 @@ class Style {
/// Default: unspecified (null)
double width;
+ /// CSS attribute "`word-spacing`"
+ ///
+ /// Inherited: yes,
+ /// Default: normal (0)
+ double wordSpacing;
+
//TODO modify these to match CSS styles
String before;
String after;
- TextDirection textDirection;
Border border;
Alignment alignment;
String markerContent;
@@ -110,23 +162,31 @@ class Style {
Style({
this.backgroundColor = Colors.transparent,
this.color,
+ this.direction,
this.display,
this.fontFamily,
+ this.fontFeatureSettings,
this.fontSize,
this.fontStyle,
this.fontWeight,
this.height,
+ this.letterSpacing,
this.listStyleType,
this.padding,
this.margin,
+ this.textAlign,
this.textDecoration,
+ this.textDecorationColor,
this.textDecorationStyle,
+ this.textDecorationThickness,
+ this.textShadow,
this.verticalAlign,
this.whiteSpace,
this.width,
+ this.wordSpacing,
+
this.before,
this.after,
- this.textDirection,
this.border,
this.alignment,
this.markerContent,
@@ -137,17 +197,25 @@ class Style {
}
}
- //TODO: all attributes of TextStyle likely have a CSS attribute and should be supported.
TextStyle generateTextStyle() {
return TextStyle(
backgroundColor: backgroundColor,
color: color,
decoration: textDecoration,
+ decorationColor: textDecorationColor,
decorationStyle: textDecorationStyle,
+ decorationThickness: textDecorationThickness,
fontFamily: fontFamily,
+ fontFeatures: fontFeatureSettings,
fontSize: fontSize?.size,
fontStyle: fontStyle,
fontWeight: fontWeight,
+ letterSpacing: letterSpacing,
+ shadows: textShadow,
+ wordSpacing: wordSpacing,
+ //TODO background
+ //TODO textBaseline
+ //TODO height
);
}
@@ -162,25 +230,33 @@ class Style {
return copyWith(
backgroundColor: other.backgroundColor,
color: other.color,
+ direction: other.direction,
display: other.display,
fontFamily: other.fontFamily,
+ fontFeatureSettings: other.fontFeatureSettings,
fontSize: other.fontSize,
fontStyle: other.fontStyle,
fontWeight: other.fontWeight,
height: other.height,
+ letterSpacing: other.letterSpacing,
listStyleType: other.listStyleType,
padding: other.padding,
//TODO merge EdgeInsets
margin: other.margin,
//TODO merge EdgeInsets
+ textAlign: other.textAlign,
textDecoration: other.textDecoration,
+ textDecorationColor: other.textDecorationColor,
textDecorationStyle: other.textDecorationStyle,
+ textDecorationThickness: other.textDecorationThickness,
+ textShadow: other.textShadow,
verticalAlign: other.verticalAlign,
whiteSpace: other.whiteSpace,
width: other.width,
+ wordSpacing: other.wordSpacing,
+
before: other.before,
after: other.after,
- textDirection: other.textDirection,
border: other.border,
//TODO merge border
alignment: other.alignment,
@@ -193,37 +269,49 @@ class Style {
return child.copyWith(
color: child.color ?? color,
+ direction: child.direction ?? direction,
fontFamily: child.fontFamily ?? fontFamily,
+ fontFeatureSettings: child.fontFeatureSettings ?? fontFeatureSettings,
fontSize: child.fontSize ?? fontSize,
fontStyle: child.fontStyle ?? fontStyle,
fontWeight: child.fontWeight ?? fontWeight,
+ letterSpacing: child.letterSpacing ?? letterSpacing,
listStyleType: child.listStyleType ?? listStyleType,
+ textAlign: child.textAlign ?? textAlign,
+ textShadow: child.textShadow ?? textShadow,
whiteSpace: child.whiteSpace ?? whiteSpace,
+ wordSpacing: child.wordSpacing ?? wordSpacing,
);
}
Style copyWith({
Color backgroundColor,
Color color,
+ TextDirection direction,
Display display,
String fontFamily,
+ List fontFeatureSettings,
FontSize fontSize,
FontStyle fontStyle,
FontWeight fontWeight,
double height,
+ double letterSpacing,
ListStyleType listStyleType,
EdgeInsets padding,
EdgeInsets margin,
+ TextAlign textAlign,
TextDecoration textDecoration,
+ Color textDecorationColor,
TextDecorationStyle textDecorationStyle,
+ double textDecorationThickness,
+ List textShadow,
VerticalAlign verticalAlign,
WhiteSpace whiteSpace,
double width,
- bool preserveWhitespace,
- int baselineOffset,
+ double wordSpacing,
+
String before,
String after,
- TextDirection textDirection,
Border border,
Alignment alignment,
String markerContent,
@@ -231,23 +319,31 @@ class Style {
return Style(
backgroundColor: backgroundColor ?? this.backgroundColor,
color: color ?? this.color,
+ direction: direction ?? this.direction,
display: display ?? this.display,
fontFamily: fontFamily ?? this.fontFamily,
+ fontFeatureSettings: fontFeatureSettings ?? this.fontFeatureSettings,
fontSize: fontSize ?? this.fontSize,
fontStyle: fontStyle ?? this.fontStyle,
fontWeight: fontWeight ?? this.fontWeight,
height: height ?? this.height,
+ letterSpacing: letterSpacing ?? this.letterSpacing,
listStyleType: listStyleType ?? this.listStyleType,
padding: padding ?? this.padding,
margin: margin ?? this.margin,
+ textAlign: textAlign ?? this.textAlign,
textDecoration: textDecoration ?? this.textDecoration,
+ textDecorationColor: textDecorationColor ?? this.textDecorationColor,
textDecorationStyle: textDecorationStyle ?? this.textDecorationStyle,
+ textDecorationThickness: textDecorationThickness ?? this.textDecorationThickness,
+ textShadow: textShadow ?? this.textShadow,
verticalAlign: verticalAlign ?? this.verticalAlign,
whiteSpace: whiteSpace ?? this.whiteSpace,
width: width ?? this.width,
+ wordSpacing: wordSpacing ?? this.wordSpacing,
+
before: before ?? this.before,
after: after ?? this.after,
- textDirection: textDirection ?? this.textDirection,
border: border ?? this.border,
alignment: alignment ?? this.alignment,
markerContent: markerContent ?? this.markerContent,
@@ -258,11 +354,17 @@ class Style {
this.backgroundColor = textStyle.backgroundColor;
this.color = textStyle.color;
this.textDecoration = textStyle.decoration;
+ this.textDecorationColor = textStyle.decorationColor;
this.textDecorationStyle = textStyle.decorationStyle;
+ this.textDecorationThickness = textStyle.decorationThickness;
this.fontFamily = textStyle.fontFamily;
+ this.fontFeatureSettings = textStyle.fontFeatures;
this.fontSize = FontSize(textStyle.fontSize);
this.fontStyle = textStyle.fontStyle;
this.fontWeight = textStyle.fontWeight;
+ this.letterSpacing = textStyle.letterSpacing;
+ this.textShadow = textStyle.shadows;
+ this.wordSpacing = textStyle.wordSpacing;
}
}
diff --git a/test/html_parser_test.dart b/test/html_parser_test.dart
index a9d734ce67..6bea22daf6 100644
--- a/test/html_parser_test.dart
+++ b/test/html_parser_test.dart
@@ -110,7 +110,7 @@ void testNewParser() {
Style style2 = Style(
before: "* ",
- textDirection: TextDirection.rtl,
+ direction: TextDirection.rtl,
fontStyle: FontStyle.italic,
);
@@ -118,7 +118,7 @@ void testNewParser() {
expect(finalStyle.display, equals(Display.BLOCK));
expect(finalStyle.before, equals("* "));
- expect(finalStyle.textDirection, equals(TextDirection.rtl));
+ expect(finalStyle.direction, equals(TextDirection.rtl));
expect(finalStyle.fontStyle, equals(FontStyle.italic));
expect(finalStyle.fontWeight, equals(FontWeight.bold));
});