From 0f3e3a4bdcb1c33599f244d0daf6fc2fc9f0313c Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 6 Sep 2022 14:31:13 -0600 Subject: [PATCH 01/82] test: Add a few more unit tests --- test/flutter_html_test.dart | 105 +++++++++++++++++++++++++++++++++ test/style/dimension_test.dart | 26 ++++---- test/style/fontsize_test.dart | 59 ++++++++++++++++++ test/utils_test.dart | 42 +++++++++++++ 4 files changed, 219 insertions(+), 13 deletions(-) create mode 100644 test/flutter_html_test.dart create mode 100644 test/style/fontsize_test.dart create mode 100644 test/utils_test.dart diff --git a/test/flutter_html_test.dart b/test/flutter_html_test.dart new file mode 100644 index 0000000000..9a5a752c70 --- /dev/null +++ b/test/flutter_html_test.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets( + "Check that widget does not fail on empty data", + (tester) async { + await tester.pumpWidget( + MaterialApp( + home: Html( + data: "", + ), + ), + ); + expect(find.text('', findRichText: true), findsOneWidget); + }, + ); + + testWidgets( + "Check that selectable widget does not fail on empty data", + (tester) async { + await tester.pumpWidget( + MaterialApp( + home: SelectableHtml( + data: '', + ), + ), + ); + expect(find.text('', findRichText: true), findsOneWidget); + }, + ); + + testWidgets( + "Check that widget displays given text", + (tester) async { + await tester.pumpWidget( + MaterialApp( + home: Html( + data: "Text", + ), + ), + ); + expect(find.text('Text', findRichText: true), findsOneWidget); + }, + ); + + testWidgets('Check that a simple element is displayed', (tester) async { + await tester.pumpWidget( + MaterialApp( + home: Html( + data: "

Text

", + ), + ), + ); + expect(find.text('Text', findRichText: true), findsOneWidget); + }); + + testWidgets('Check that a simple element is hidden when tagsList does not contain it', (tester) async { + await tester.pumpWidget( + MaterialApp( + home: Html( + data: "

Text

", + tagsList: ['div'], //Anything but `p` + ), + ), + ); + expect(find.text('Text', findRichText: true), findsNothing); + }); + + testWidgets('Check that a simple element is displayed when it is included in tagsList', (tester) async { + await tester.pumpWidget( + MaterialApp( + home: Html( + data: "

Text

", + tagsList: ['html', 'body', 'p'], + ), + ), + ); + expect(find.text('Text', findRichText: true), findsOneWidget); + }); + + testWidgets('Check that a custom element is not displayed', (tester) async { + await tester.pumpWidget( + MaterialApp( + home: Html( + data: "Text", + ), + ), + ); + expect(find.text('Text', findRichText: true), findsNothing); + }); + + testWidgets('Check that a custom element is not displayed', (tester) async { + await tester.pumpWidget( + MaterialApp( + home: Html( + data: "Text", + tagsList: Html.tags..add('custom'), + ), + ), + ); + expect(find.text('Text', findRichText: true), findsOneWidget); + }); +} diff --git a/test/style/dimension_test.dart b/test/style/dimension_test.dart index b36fa475bc..cf239249f2 100644 --- a/test/style/dimension_test.dart +++ b/test/style/dimension_test.dart @@ -28,17 +28,17 @@ void main() { expect(lengthPercent.unit, equals(Unit.px)); }); - // test("Pass in invalid unit", () { - // expect(() => Length(nonZeroNumber, Unit.percent), throwsAssertionError); - // }); - - // test("Pass in invalid unit with zero", () { - // expect(() => Length(0, Unit.percent), throwsAssertionError); - // }); - - // test("Pass in a valid unit", () { - // final lengthPercent = LengthOrPercent(nonZeroNumber, Unit.percent); - // expect(lengthPercent.value, equals(nonZeroNumber)); - // expect(lengthPercent.unit, equals(Unit.percent)); - // }); + test("Pass in invalid unit", () { + expect(() => Length(nonZeroNumber, Unit.percent), throwsAssertionError); + }); + + test("Pass in invalid unit with zero", () { + expect(() => Length(0, Unit.percent), throwsAssertionError); + }); + + test("Pass in a valid unit", () { + final lengthPercent = LengthOrPercent(nonZeroNumber, Unit.percent); + expect(lengthPercent.value, equals(nonZeroNumber)); + expect(lengthPercent.unit, equals(Unit.percent)); + }); } \ No newline at end of file diff --git a/test/style/fontsize_test.dart b/test/style/fontsize_test.dart new file mode 100644 index 0000000000..e985d73706 --- /dev/null +++ b/test/style/fontsize_test.dart @@ -0,0 +1,59 @@ +import 'package:flutter_html/src/style/fontsize.dart'; +import 'package:flutter_html/src/style/length.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('Check basic FontSize inheritance', () { + final FontSize parent = FontSize(16); + final FontSize? child = null; + + final result = FontSize.inherit(parent, child); + + expect(result?.value, equals(16)); + }); + + test('Check double null FontSize inheritance', () { + final FontSize? parent = null; + final FontSize? child = null; + + final result = FontSize.inherit(parent, child); + + expect(result?.value, equals(null)); + }); + + test('Check basic em inheritance', () { + final FontSize? parent = FontSize(16); + final FontSize? child = FontSize(1, Unit.em); + + final result = FontSize.inherit(parent, child); + + expect(result?.value, equals(16)); + }); + + test('Check factor em inheritance', () { + final FontSize? parent = FontSize(16); + final FontSize? child = FontSize(0.5, Unit.em); + + final result = FontSize.inherit(parent, child); + + expect(result?.value, equals(8)); + }); + + test('Check basic % inheritance', () { + final FontSize? parent = FontSize(16); + final FontSize? child = FontSize(100, Unit.percent); + + final result = FontSize.inherit(parent, child); + + expect(result?.value, equals(16)); + }); + + test('Check scaled % inheritance', () { + final FontSize? parent = FontSize(16); + final FontSize? child = FontSize(50, Unit.percent); + + final result = FontSize.inherit(parent, child); + + expect(result?.value, equals(8)); + }); +} \ No newline at end of file diff --git a/test/utils_test.dart b/test/utils_test.dart new file mode 100644 index 0000000000..c4cec61f93 --- /dev/null +++ b/test/utils_test.dart @@ -0,0 +1,42 @@ +import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter_html/src/css_parser.dart'; +import 'package:flutter_html/src/utils.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// Tests the file lib/src/utils.dart + +void main() { + test('Tests that namedColors returns a valid color', () { + expect(ExpressionMapping.namedColorToColor('red'), equals(ExpressionMapping.stringToColor(namedColors['Red']!))); + expect(namedColors['Red'], equals('#FF0000')); + }); + + test('CustomBorderSide does not allow negative width', () { + expect(() => CustomBorderSide(width: -5), throwsAssertionError); + expect(CustomBorderSide(width: 0), TypeMatcher()); + expect(CustomBorderSide(width: 5), TypeMatcher()); + }); + + const originalString = 'Hello'; + const uppercaseString = 'HELLO'; + const lowercaseString = 'hello'; + + test('TextTransformUtil returns self if transform is null', () { + expect(originalString.transformed(null), equals(originalString)); + }); + + test('TextTransformUtil uppercases correctly', () { + expect(originalString.transformed(TextTransform.uppercase), equals(uppercaseString)); + }); + + test('TextTransformUtil lowercases correctly', () { + expect(originalString.transformed(TextTransform.lowercase), equals(lowercaseString)); + }); + + const originalLongString = 'Hello, world! pub.dev'; + const capitalizedLongString = 'Hello, World! Pub.Dev'; + + test('TextTransformUtil capitalizs correctly', () { + expect(originalLongString.transformed(TextTransform.capitalize), equals(capitalizedLongString)); + }); +} \ No newline at end of file From a62449a77c18701a0faf8ffd650f9c535b2d006c Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Sat, 17 Sep 2022 06:55:46 -0600 Subject: [PATCH 02/82] fix: Change CSSBoxWidget to CssBoxWidget --- lib/custom_render.dart | 8 ++++---- lib/html_parser.dart | 4 ++-- lib/src/css_box_widget.dart | 7 +++---- lib/src/layout_element.dart | 4 ++-- lib/src/replaced_element.dart | 6 ++---- packages/flutter_html_audio/lib/flutter_html_audio.dart | 2 +- packages/flutter_html_iframe/lib/iframe_mobile.dart | 2 +- packages/flutter_html_iframe/lib/iframe_web.dart | 2 +- packages/flutter_html_table/lib/flutter_html_table.dart | 4 ++-- 9 files changed, 18 insertions(+), 21 deletions(-) diff --git a/lib/custom_render.dart b/lib/custom_render.dart index 15ba16aeb9..27d15f13ff 100644 --- a/lib/custom_render.dart +++ b/lib/custom_render.dart @@ -131,7 +131,7 @@ CustomRender blockElementRender({Style? style, List? children}) => return WidgetSpan( alignment: PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic, - child: CSSBoxWidget.withInlineSpanChildren( + child: CssBoxWidget.withInlineSpanChildren( key: context.key, style: style ?? context.tree.style, shrinkWrap: context.parser.shrinkWrap, @@ -157,7 +157,7 @@ CustomRender listElementRender( {Style? style, Widget? child, List? children}) => CustomRender.inlineSpan( inlineSpan: (context, buildChildren) => WidgetSpan( - child: CSSBoxWidget( + child: CssBoxWidget( key: context.key, style: style ?? context.tree.style, shrinkWrap: context.parser.shrinkWrap, @@ -206,7 +206,7 @@ CustomRender listElementRender( ? 10.0 : 0.0) : EdgeInsets.zero, - child: CSSBoxWidget.withInlineSpanChildren( + child: CssBoxWidget.withInlineSpanChildren( children: _getListElementChildren( style?.listStylePosition ?? context.tree.style.listStylePosition, @@ -472,7 +472,7 @@ CustomRender verticalAlignRender( key: context.key, offset: Offset( 0, verticalOffset ?? _getVerticalOffset(context.tree)), - child: CSSBoxWidget.withInlineSpanChildren( + child: CssBoxWidget.withInlineSpanChildren( children: children ?? buildChildren.call(), style: context.style, ), diff --git a/lib/html_parser.dart b/lib/html_parser.dart index dfbd996fae..7ed4e959af 100644 --- a/lib/html_parser.dart +++ b/lib/html_parser.dart @@ -101,7 +101,7 @@ class HtmlParser extends StatelessWidget { processedTree, ); - return CSSBoxWidget.withInlineSpanChildren( + return CssBoxWidget.withInlineSpanChildren( style: processedTree.style, children: [parsedTree], selectable: selectable, @@ -364,7 +364,7 @@ class HtmlParser extends StatelessWidget { .call(newContext, buildChildren); } return WidgetSpan( - child: CSSBoxWidget( + child: CssBoxWidget( style: tree.style, shrinkWrap: newContext.parser.shrinkWrap, child: diff --git a/lib/src/css_box_widget.dart b/lib/src/css_box_widget.dart index 785b2b46c5..5bfffd82da 100644 --- a/lib/src/css_box_widget.dart +++ b/lib/src/css_box_widget.dart @@ -4,8 +4,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_html/flutter_html.dart'; -class CSSBoxWidget extends StatelessWidget { - CSSBoxWidget({ +class CssBoxWidget extends StatelessWidget { + CssBoxWidget({ this.key, required this.child, required this.style, @@ -15,7 +15,7 @@ class CSSBoxWidget extends StatelessWidget { }) : super(key: key); /// Generates a CSSBoxWidget that contains a list of InlineSpan children. - CSSBoxWidget.withInlineSpanChildren({ + CssBoxWidget.withInlineSpanChildren({ this.key, required List children, required this.style, @@ -696,7 +696,6 @@ extension Normalize on Dimension { } double _calculateEmValue(Style style, BuildContext buildContext) { - //TODO is there a better value for this? return (style.fontSize?.emValue ?? 16) * MediaQuery.textScaleFactorOf(buildContext) * MediaQuery.of(buildContext).devicePixelRatio; diff --git a/lib/src/layout_element.dart b/lib/src/layout_element.dart index 778cb93516..d366ce1fc3 100644 --- a/lib/src/layout_element.dart +++ b/lib/src/layout_element.dart @@ -154,13 +154,13 @@ class DetailsContentElement extends LayoutElement { expandedAlignment: Alignment.centerLeft, title: elementList.isNotEmpty == true && elementList.first.localName == "summary" - ? CSSBoxWidget.withInlineSpanChildren( + ? CssBoxWidget.withInlineSpanChildren( children: firstChild == null ? [] : [firstChild], style: style, ) : Text("Details"), children: [ - CSSBoxWidget.withInlineSpanChildren( + CssBoxWidget.withInlineSpanChildren( children: getChildren( childrenList, context, diff --git a/lib/src/replaced_element.dart b/lib/src/replaced_element.dart index c33f9ff3cb..3234da0f1d 100644 --- a/lib/src/replaced_element.dart +++ b/lib/src/replaced_element.dart @@ -112,9 +112,8 @@ class RubyElement extends ReplacedElement { child: Center( child: Transform( transform: Matrix4.translationValues(0, -(rubyYPos), 0), - child: CSSBoxWidget( + child: CssBoxWidget( style: c.style, - //TODO do any other attributes apply? child: Text( c.element!.innerHtml, style: c.style @@ -125,8 +124,7 @@ class RubyElement extends ReplacedElement { ), ), ), - CSSBoxWidget( - //TODO do any other styles apply? Does ruby still work? + CssBoxWidget( style: context.style, child: node is TextContentElement ? Text( diff --git a/packages/flutter_html_audio/lib/flutter_html_audio.dart b/packages/flutter_html_audio/lib/flutter_html_audio.dart index d9576f0270..c542f12242 100644 --- a/packages/flutter_html_audio/lib/flutter_html_audio.dart +++ b/packages/flutter_html_audio/lib/flutter_html_audio.dart @@ -81,7 +81,7 @@ class _AudioWidgetState extends State { return Container(height: 0, width: 0); } - return CSSBoxWidget( + return CssBoxWidget( key: widget.context.key, style: widget.context.style, child: ChewieAudio( diff --git a/packages/flutter_html_iframe/lib/iframe_mobile.dart b/packages/flutter_html_iframe/lib/iframe_mobile.dart index 80b20cd09c..b35f7c7e3b 100644 --- a/packages/flutter_html_iframe/lib/iframe_mobile.dart +++ b/packages/flutter_html_iframe/lib/iframe_mobile.dart @@ -15,7 +15,7 @@ CustomRender iframeRender({NavigationDelegate? navigationDelegate}) => return Container( width: givenWidth ?? (givenHeight ?? 150) * 2, height: givenHeight ?? (givenWidth ?? 300) / 2, - child: CSSBoxWidget( + child: CssBoxWidget( style: context.style, childIsReplaced: true, child: WebView( diff --git a/packages/flutter_html_iframe/lib/iframe_web.dart b/packages/flutter_html_iframe/lib/iframe_web.dart index 12476c3edd..aaf81c23ff 100644 --- a/packages/flutter_html_iframe/lib/iframe_web.dart +++ b/packages/flutter_html_iframe/lib/iframe_web.dart @@ -35,7 +35,7 @@ CustomRender iframeRender({NavigationDelegate? navigationDelegate}) => (double.tryParse(context.tree.element?.attributes['width'] ?? "") ?? 300) / 2, - child: CSSBoxWidget( + child: CssBoxWidget( style: context.style, childIsReplaced: true, child: Directionality( diff --git a/packages/flutter_html_table/lib/flutter_html_table.dart b/packages/flutter_html_table/lib/flutter_html_table.dart index 353f3117c0..32c6a66f8b 100644 --- a/packages/flutter_html_table/lib/flutter_html_table.dart +++ b/packages/flutter_html_table/lib/flutter_html_table.dart @@ -116,7 +116,7 @@ Widget _layoutCells(RenderContext context, BoxConstraints constraints) { columnColspanOffset[columni].clamp(1, columnMax - columni - 1); } cells.add(GridPlacement( - child: CSSBoxWidget( + child: CssBoxWidget( style: child.style .merge(row.style), //TODO padding/decoration(color/border) child: SizedBox.expand( @@ -124,7 +124,7 @@ Widget _layoutCells(RenderContext context, BoxConstraints constraints) { alignment: child.style.alignment ?? context.style.alignment ?? Alignment.centerLeft, - child: CSSBoxWidget.withInlineSpanChildren( + child: CssBoxWidget.withInlineSpanChildren( children: [context.parser.parseTree(context, child)], style: child.style, //TODO updated this. Does it work? ), From 9dc7f08ca238ff6a93314be5de716ad4e3baebb8 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Sat, 17 Sep 2022 07:13:57 -0600 Subject: [PATCH 03/82] fix: Use enum instead of const int internally in length.dart --- lib/src/style/length.dart | 47 ++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/lib/src/style/length.dart b/lib/src/style/length.dart index dbc47f71d4..3fab69263d 100644 --- a/lib/src/style/length.dart +++ b/lib/src/style/length.dart @@ -1,27 +1,35 @@ -/// Increase new base unit types' values by a factor of 2 each time. -const int _percent = 0x1; -const int _length = 0x2; -const int _auto = 0x4; +/// These are the base unit types +enum _UnitType { + percent, + length, + auto, + lengthPercent(children: [_UnitType.length, _UnitType.percent]), + lengthPercentAuto(children: [_UnitType.length, _UnitType.percent, _UnitType.auto]); -/// These values are combinations of the base unit-types -const int _lengthPercent = _length | _percent; -const int _lengthPercentAuto = _lengthPercent | _auto; + final List<_UnitType> children; + + const _UnitType({this.children = const []}); + + bool matches(_UnitType other) { + return this == other || children.contains(other); + } +} /// A Unit represents a CSS unit enum Unit { //ch, - em(_length), + em(_UnitType.length), //ex, - percent(_percent), - px(_length), - rem(_length), + percent(_UnitType.percent), + px(_UnitType.length), + rem(_UnitType.length), //Q, //vh, //vw, - auto(_auto); + auto(_UnitType.auto); const Unit(this.unitType); - final int unitType; + final _UnitType unitType; } /// Represents a CSS dimension https://drafts.csswg.org/css-values/#dimensions @@ -29,16 +37,15 @@ abstract class Dimension { double value; Unit unit; - Dimension(this.value, this.unit, int _dimensionUnitType) - : assert( - identical((unit.unitType | _dimensionUnitType), _dimensionUnitType), - "This dimension was given a Unit that isn't specified."); + Dimension(this.value, this.unit, _UnitType _dimensionUnitType) + : assert(_dimensionUnitType.matches(unit.unitType), + "This Dimension was given a Unit that isn't specified."); } /// This dimension takes a value with a length unit such as px or em. Note that /// these can be fixed or relative (but they must not be a percent) class Length extends Dimension { - Length(double value, [Unit unit = Unit.px]) : super(value, unit, _length); + Length(double value, [Unit unit = Unit.px]) : super(value, unit, _UnitType.length); } /// This dimension takes a value with a length-percent unit such as px or em @@ -46,10 +53,10 @@ class Length extends Dimension { /// percent) class LengthOrPercent extends Dimension { LengthOrPercent(double value, [Unit unit = Unit.px]) - : super(value, unit, _lengthPercent); + : super(value, unit, _UnitType.lengthPercent); } class AutoOrLengthOrPercent extends Dimension { AutoOrLengthOrPercent(double value, [Unit unit = Unit.px]) - : super(value, unit, _lengthPercentAuto); + : super(value, unit, _UnitType.lengthPercentAuto); } From 7581ea798744b2830affaaf75bbdff016b03f7af Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Sat, 17 Sep 2022 08:08:00 -0600 Subject: [PATCH 04/82] fix: Apply margins to properly --- .../lib/flutter_html_table.dart | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/packages/flutter_html_table/lib/flutter_html_table.dart b/packages/flutter_html_table/lib/flutter_html_table.dart index 32c6a66f8b..8a207abbf3 100644 --- a/packages/flutter_html_table/lib/flutter_html_table.dart +++ b/packages/flutter_html_table/lib/flutter_html_table.dart @@ -9,25 +9,12 @@ import 'package:flutter_html/flutter_html.dart'; /// The CustomRender function that will render the
HTML tag CustomRender tableRender() => CustomRender.widget(widget: (context, buildChildren) { - return Container( + return CssBoxWidget( key: context.key, - //TODO(Sub6Resources): This needs to be computed with Units!! - margin: EdgeInsets.only( - left: context.style.margin?.left?.value.abs() ?? 0, - right: context.style.margin?.right?.value.abs() ?? 0, - bottom: context.style.margin?.bottom?.value.abs() ?? 0, - top: context.style.margin?.bottom?.value.abs() ?? 0, - ), - padding: context.style.padding?.nonNegative, - alignment: context.style.alignment, - decoration: BoxDecoration( - color: context.style.backgroundColor, - border: context.style.border, - ), - width: context.style.width?.value, //TODO calculate actual value - height: context.style.height?.value, //TODO calculate actual value + style: context.style, child: LayoutBuilder( - builder: (_, constraints) => _layoutCells(context, constraints)), + builder: (_, constraints) => _layoutCells(context, constraints), + ), ); }); From 18c77db1f486d96af2dcf429de4dba61e6d76a0e Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 17:07:36 -0600 Subject: [PATCH 05/82] chore: Add basic melos support --- .circleci/config.yml | 35 ++++++++++++++++++++++++++++------- .gitignore | 4 ++++ combine_coverage.sh | 14 ++++++++++++++ melos.yaml | 31 +++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 7 deletions(-) create mode 100755 combine_coverage.sh create mode 100644 melos.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml index 16c40c66b9..ac1b6f299e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,13 +1,34 @@ version: 2.1 orbs: codecov: codecov/codecov@1.0.2 +executors: + default-executor: + docker: + - image: cirrusci/flutter:stable + resource_class: large + shell: /bin/bash jobs: build: - docker: - - image: cirrusci/flutter + executor: default-executor steps: - - checkout - - run: flutter --version - - run: flutter test --coverage - - codecov/upload: - file: coverage/lcov.info + - checkout + - run: flutter --version + - run: + name: Set up environment + command: | + echo 'export PATH=$HOME/.pub-cache/bin:$PATH' >> $BASH_ENV + source $BASH_ENV + - run: + name: Setup melos + command: | + flutter pub global activate melos + melos --version + melos bootstrap + - run: + name: Run Test Suite + command: melos run test + - run: + name: Generate Coverage Report + command: melos run gen_coverage + - codecov/upload: + file: coverage_report/lcov.info diff --git a/.gitignore b/.gitignore index 34258e6aad..caf0d56dbf 100644 --- a/.gitignore +++ b/.gitignore @@ -151,3 +151,7 @@ modules.xml **/flutter_export_environment.sh /example/ios/Flutter/Flutter.podspec + +packages/**/pubspec_overrides.yaml +./pubspec_overrides.yaml +/example/pubspec_overrides.yaml \ No newline at end of file diff --git a/combine_coverage.sh b/combine_coverage.sh new file mode 100755 index 0000000000..8831e1fa6a --- /dev/null +++ b/combine_coverage.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +escapedPath="$(echo `pwd` | sed 's/\//\\\//g')" + +if grep flutter pubspec.yaml > /dev/null; then + if [ -d "coverage" ]; then + # combine line coverage info from package tests to a common file + if [ ! -d "$MELOS_ROOT_PATH/coverage_report" ]; then + mkdir "$MELOS_ROOT_PATH/coverage_report" + fi + sed "s/^SF:lib/SF:$escapedPath\/lib/g" coverage/lcov.info >> "$MELOS_ROOT_PATH/coverage_report/lcov.info" + rm -rf "coverage" + fi +fi \ No newline at end of file diff --git a/melos.yaml b/melos.yaml new file mode 100644 index 0000000000..31db195878 --- /dev/null +++ b/melos.yaml @@ -0,0 +1,31 @@ +name: flutter_html +repository: https://github.com/sub6resources/flutter_html + +packages: + - packages/* + - . + - example + +command: + bootstrap: + usePubspecOverrides: true + runPubGetInParallel: false + version: + includeCommitId: true + +scripts: + analyze: + exec: flutter analyze . --fatal-infos + + test:selective_unit_test: + run: melos exec --dir-exists="test" --fail-fast -- flutter test --no-pub --coverage + description: Run Flutter tests for a specific package in this project. + select-package: + flutter: true + dir-exists: test + + test: + run: melos run test:selective_unit_test --no-select + description: Run all Flutter tests in this project. + + gen_coverage: melos exec -- "\$MELOS_ROOT_PATH/combine_coverage.sh" \ No newline at end of file From 92e4bd91ccfd92447e6693c7c09dbd6f26d65701 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 11:12:25 -0600 Subject: [PATCH 06/82] Add analysis and code format checks --- .circleci/config.yml | 6 ++++++ packages/flutter_html_all/pubspec_overrides.yaml | 16 ++++++++++++++++ .../flutter_html_audio/pubspec_overrides.yaml | 4 ++++ .../flutter_html_iframe/pubspec_overrides.yaml | 4 ++++ .../flutter_html_math/pubspec_overrides.yaml | 4 ++++ packages/flutter_html_svg/pubspec_overrides.yaml | 4 ++++ .../flutter_html_table/pubspec_overrides.yaml | 4 ++++ .../flutter_html_video/pubspec_overrides.yaml | 4 ++++ 8 files changed, 46 insertions(+) create mode 100644 packages/flutter_html_all/pubspec_overrides.yaml create mode 100644 packages/flutter_html_audio/pubspec_overrides.yaml create mode 100644 packages/flutter_html_iframe/pubspec_overrides.yaml create mode 100644 packages/flutter_html_math/pubspec_overrides.yaml create mode 100644 packages/flutter_html_svg/pubspec_overrides.yaml create mode 100644 packages/flutter_html_table/pubspec_overrides.yaml create mode 100644 packages/flutter_html_video/pubspec_overrides.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml index ac1b6f299e..010547222b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -32,3 +32,9 @@ jobs: command: melos run gen_coverage - codecov/upload: file: coverage_report/lcov.info + - run: + name: Run flutter analyze + command: melos run analyze + - run: + name: Check That Flutter Code is Formatted Correctly + command: flutter format -o none --set-exit-if-changed . diff --git a/packages/flutter_html_all/pubspec_overrides.yaml b/packages/flutter_html_all/pubspec_overrides.yaml new file mode 100644 index 0000000000..54b3d46a71 --- /dev/null +++ b/packages/flutter_html_all/pubspec_overrides.yaml @@ -0,0 +1,16 @@ +# melos_managed_dependency_overrides: flutter_html,flutter_html_audio,flutter_html_iframe,flutter_html_math,flutter_html_svg,flutter_html_table,flutter_html_video +dependency_overrides: + flutter_html: + path: ../.. + flutter_html_audio: + path: ../flutter_html_audio + flutter_html_iframe: + path: ../flutter_html_iframe + flutter_html_math: + path: ../flutter_html_math + flutter_html_svg: + path: ../flutter_html_svg + flutter_html_table: + path: ../flutter_html_table + flutter_html_video: + path: ../flutter_html_video diff --git a/packages/flutter_html_audio/pubspec_overrides.yaml b/packages/flutter_html_audio/pubspec_overrides.yaml new file mode 100644 index 0000000000..65126a9bbf --- /dev/null +++ b/packages/flutter_html_audio/pubspec_overrides.yaml @@ -0,0 +1,4 @@ +# melos_managed_dependency_overrides: flutter_html +dependency_overrides: + flutter_html: + path: ../.. diff --git a/packages/flutter_html_iframe/pubspec_overrides.yaml b/packages/flutter_html_iframe/pubspec_overrides.yaml new file mode 100644 index 0000000000..65126a9bbf --- /dev/null +++ b/packages/flutter_html_iframe/pubspec_overrides.yaml @@ -0,0 +1,4 @@ +# melos_managed_dependency_overrides: flutter_html +dependency_overrides: + flutter_html: + path: ../.. diff --git a/packages/flutter_html_math/pubspec_overrides.yaml b/packages/flutter_html_math/pubspec_overrides.yaml new file mode 100644 index 0000000000..65126a9bbf --- /dev/null +++ b/packages/flutter_html_math/pubspec_overrides.yaml @@ -0,0 +1,4 @@ +# melos_managed_dependency_overrides: flutter_html +dependency_overrides: + flutter_html: + path: ../.. diff --git a/packages/flutter_html_svg/pubspec_overrides.yaml b/packages/flutter_html_svg/pubspec_overrides.yaml new file mode 100644 index 0000000000..65126a9bbf --- /dev/null +++ b/packages/flutter_html_svg/pubspec_overrides.yaml @@ -0,0 +1,4 @@ +# melos_managed_dependency_overrides: flutter_html +dependency_overrides: + flutter_html: + path: ../.. diff --git a/packages/flutter_html_table/pubspec_overrides.yaml b/packages/flutter_html_table/pubspec_overrides.yaml new file mode 100644 index 0000000000..65126a9bbf --- /dev/null +++ b/packages/flutter_html_table/pubspec_overrides.yaml @@ -0,0 +1,4 @@ +# melos_managed_dependency_overrides: flutter_html +dependency_overrides: + flutter_html: + path: ../.. diff --git a/packages/flutter_html_video/pubspec_overrides.yaml b/packages/flutter_html_video/pubspec_overrides.yaml new file mode 100644 index 0000000000..65126a9bbf --- /dev/null +++ b/packages/flutter_html_video/pubspec_overrides.yaml @@ -0,0 +1,4 @@ +# melos_managed_dependency_overrides: flutter_html +dependency_overrides: + flutter_html: + path: ../.. From bce44d420d14dfb351ce696944eac82cebe61b1b Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 11:31:31 -0600 Subject: [PATCH 07/82] chore: Add analysis_options.yaml to each package --- analysis_options.yaml | 29 +++++++++++++++++++ example/pubspec.yaml | 1 + .../flutter_html_all/analysis_options.yaml | 29 +++++++++++++++++++ packages/flutter_html_all/pubspec.yaml | 1 + .../flutter_html_audio/analysis_options.yaml | 29 +++++++++++++++++++ packages/flutter_html_audio/pubspec.yaml | 1 + .../flutter_html_iframe/analysis_options.yaml | 29 +++++++++++++++++++ packages/flutter_html_iframe/pubspec.yaml | 1 + .../flutter_html_math/analysis_options.yaml | 29 +++++++++++++++++++ packages/flutter_html_math/pubspec.yaml | 1 + .../flutter_html_svg/analysis_options.yaml | 29 +++++++++++++++++++ packages/flutter_html_svg/pubspec.yaml | 1 + .../flutter_html_table/analysis_options.yaml | 29 +++++++++++++++++++ packages/flutter_html_table/pubspec.yaml | 1 + .../flutter_html_video/analysis_options.yaml | 29 +++++++++++++++++++ packages/flutter_html_video/pubspec.yaml | 1 + pubspec.yaml | 1 + 17 files changed, 241 insertions(+) create mode 100644 analysis_options.yaml create mode 100644 packages/flutter_html_all/analysis_options.yaml create mode 100644 packages/flutter_html_audio/analysis_options.yaml create mode 100644 packages/flutter_html_iframe/analysis_options.yaml create mode 100644 packages/flutter_html_math/analysis_options.yaml create mode 100644 packages/flutter_html_svg/analysis_options.yaml create mode 100644 packages/flutter_html_table/analysis_options.yaml create mode 100644 packages/flutter_html_video/analysis_options.yaml diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000000..f065cf1ea4 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options \ No newline at end of file diff --git a/example/pubspec.yaml b/example/pubspec.yaml index ffde7654f9..71e0332d40 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^2.0.1 flutter: diff --git a/packages/flutter_html_all/analysis_options.yaml b/packages/flutter_html_all/analysis_options.yaml new file mode 100644 index 0000000000..f065cf1ea4 --- /dev/null +++ b/packages/flutter_html_all/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options \ No newline at end of file diff --git a/packages/flutter_html_all/pubspec.yaml b/packages/flutter_html_all/pubspec.yaml index ab76f12164..82e60c2be3 100644 --- a/packages/flutter_html_all/pubspec.yaml +++ b/packages/flutter_html_all/pubspec.yaml @@ -34,5 +34,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^2.0.1 flutter: diff --git a/packages/flutter_html_audio/analysis_options.yaml b/packages/flutter_html_audio/analysis_options.yaml new file mode 100644 index 0000000000..f065cf1ea4 --- /dev/null +++ b/packages/flutter_html_audio/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options \ No newline at end of file diff --git a/packages/flutter_html_audio/pubspec.yaml b/packages/flutter_html_audio/pubspec.yaml index 426fafb794..9f2ec7000e 100644 --- a/packages/flutter_html_audio/pubspec.yaml +++ b/packages/flutter_html_audio/pubspec.yaml @@ -21,5 +21,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^2.0.1 flutter: diff --git a/packages/flutter_html_iframe/analysis_options.yaml b/packages/flutter_html_iframe/analysis_options.yaml new file mode 100644 index 0000000000..f065cf1ea4 --- /dev/null +++ b/packages/flutter_html_iframe/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options \ No newline at end of file diff --git a/packages/flutter_html_iframe/pubspec.yaml b/packages/flutter_html_iframe/pubspec.yaml index d7207c433c..95bb7b6f28 100644 --- a/packages/flutter_html_iframe/pubspec.yaml +++ b/packages/flutter_html_iframe/pubspec.yaml @@ -20,5 +20,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^2.0.1 flutter: diff --git a/packages/flutter_html_math/analysis_options.yaml b/packages/flutter_html_math/analysis_options.yaml new file mode 100644 index 0000000000..f065cf1ea4 --- /dev/null +++ b/packages/flutter_html_math/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options \ No newline at end of file diff --git a/packages/flutter_html_math/pubspec.yaml b/packages/flutter_html_math/pubspec.yaml index 8a2fa63e50..e67e69a4eb 100644 --- a/packages/flutter_html_math/pubspec.yaml +++ b/packages/flutter_html_math/pubspec.yaml @@ -20,5 +20,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^2.0.1 flutter: diff --git a/packages/flutter_html_svg/analysis_options.yaml b/packages/flutter_html_svg/analysis_options.yaml new file mode 100644 index 0000000000..f065cf1ea4 --- /dev/null +++ b/packages/flutter_html_svg/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options \ No newline at end of file diff --git a/packages/flutter_html_svg/pubspec.yaml b/packages/flutter_html_svg/pubspec.yaml index f00202478d..b1b4089520 100644 --- a/packages/flutter_html_svg/pubspec.yaml +++ b/packages/flutter_html_svg/pubspec.yaml @@ -20,5 +20,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^2.0.1 flutter: diff --git a/packages/flutter_html_table/analysis_options.yaml b/packages/flutter_html_table/analysis_options.yaml new file mode 100644 index 0000000000..f065cf1ea4 --- /dev/null +++ b/packages/flutter_html_table/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options \ No newline at end of file diff --git a/packages/flutter_html_table/pubspec.yaml b/packages/flutter_html_table/pubspec.yaml index 564dfb9fe2..4bee5c2ab0 100644 --- a/packages/flutter_html_table/pubspec.yaml +++ b/packages/flutter_html_table/pubspec.yaml @@ -21,5 +21,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^2.0.1 flutter: diff --git a/packages/flutter_html_video/analysis_options.yaml b/packages/flutter_html_video/analysis_options.yaml new file mode 100644 index 0000000000..f065cf1ea4 --- /dev/null +++ b/packages/flutter_html_video/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options \ No newline at end of file diff --git a/packages/flutter_html_video/pubspec.yaml b/packages/flutter_html_video/pubspec.yaml index 50354655c1..9a07f6052c 100644 --- a/packages/flutter_html_video/pubspec.yaml +++ b/packages/flutter_html_video/pubspec.yaml @@ -21,5 +21,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^2.0.1 flutter: diff --git a/pubspec.yaml b/pubspec.yaml index 6f1baadd9f..d1ae1407ab 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,5 +26,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^2.0.1 flutter: From 3defeecf319a49f6855b67bd43772b113016e291 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 12:02:02 -0600 Subject: [PATCH 08/82] Fix analysis warning --- lib/src/css_box_widget.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/src/css_box_widget.dart b/lib/src/css_box_widget.dart index 5bfffd82da..416e3fe11e 100644 --- a/lib/src/css_box_widget.dart +++ b/lib/src/css_box_widget.dart @@ -683,6 +683,10 @@ class _RenderCSSBox extends RenderBox extension Normalize on Dimension { void normalize(double emValue) { switch (this.unit) { + case Unit.rem: + // Because CSSBoxWidget doesn't have any information about any + // sort of tree structure, treat rem the same as em. The HtmlParser + // widget handles rem/em values before they get to CSSBoxWidget. case Unit.em: this.value *= emValue; this.unit = Unit.px; From e1a5d3d3bcdcf6315da907ab48cb3c373cc5d30b Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 15:38:25 -0600 Subject: [PATCH 09/82] Fix analysis hints in lib/ --- lib/custom_render.dart | 54 ++-- lib/flutter_html.dart | 22 +- lib/html_parser.dart | 153 +++++----- lib/src/css_box_widget.dart | 68 ++--- lib/src/css_parser.dart | 182 ++++++------ lib/src/html_elements.dart | 472 ++++++++++++------------------ lib/src/interactable_element.dart | 2 +- lib/src/layout_element.dart | 8 +- lib/src/replaced_element.dart | 20 +- lib/src/style/fontsize.dart | 4 +- lib/src/style/length.dart | 34 +-- lib/src/styled_element.dart | 85 +++--- lib/style.dart | 88 +++--- 13 files changed, 554 insertions(+), 638 deletions(-) diff --git a/lib/custom_render.dart b/lib/custom_render.dart index 27d15f13ff..2a0bdea729 100644 --- a/lib/custom_render.dart +++ b/lib/custom_render.dart @@ -16,14 +16,14 @@ CustomRenderMatcher tagMatcher(String tag) => (context) { }; CustomRenderMatcher blockElementMatcher() => (context) { - return (context.tree.style.display == Display.BLOCK || - context.tree.style.display == Display.INLINE_BLOCK) && + return (context.tree.style.display == Display.block || + context.tree.style.display == Display.inlineBlock) && (context.tree.children.isNotEmpty || context.tree.element?.localName == "hr"); }; CustomRenderMatcher listElementMatcher() => (context) { - return context.tree.style.display == Display.LIST_ITEM; + return context.tree.style.display == Display.listItem; }; CustomRenderMatcher replacedElementMatcher() => (context) { @@ -44,7 +44,7 @@ CustomRenderMatcher dataUriMatcher( }; CustomRenderMatcher networkSourceMatcher({ - List schemas: const ["https", "http"], + List schemas = const ["https", "http"], List? domains, String? extension, }) => @@ -81,7 +81,7 @@ CustomRenderMatcher layoutElementMatcher() => (context) { CustomRenderMatcher verticalAlignMatcher() => (context) { return context.tree.style.verticalAlign != null && - context.tree.style.verticalAlign != VerticalAlign.BASELINE; + context.tree.style.verticalAlign != VerticalAlign.baseline; }; CustomRenderMatcher fallbackMatcher() => (context) { @@ -120,10 +120,10 @@ CustomRender blockElementRender({Style? style, List? children}) => .expandIndexed((i, childTree) => [ context.parser.parseTree(context, childTree), if (i != context.tree.children.length - 1 && - childTree.style.display == Display.BLOCK && + childTree.style.display == Display.block && childTree.element?.localName != "html" && childTree.element?.localName != "body") - TextSpan(text: "\n"), + const TextSpan(text: "\n"), ]) .toList(), ); @@ -136,17 +136,17 @@ CustomRender blockElementRender({Style? style, List? children}) => style: style ?? context.tree.style, shrinkWrap: context.parser.shrinkWrap, childIsReplaced: - REPLACED_EXTERNAL_ELEMENTS.contains(context.tree.name), + HtmlElements.replacedExternalElements.contains(context.tree.name), children: children ?? context.tree.children .expandIndexed((i, childTree) => [ context.parser.parseTree(context, childTree), //TODO can this newline be added in a different step? if (i != context.tree.children.length - 1 && - childTree.style.display == Display.BLOCK && + childTree.style.display == Display.block && childTree.element?.localName != "html" && childTree.element?.localName != "body") - TextSpan(text: "\n"), + const TextSpan(text: "\n"), ]) .toList(), ), @@ -168,7 +168,7 @@ CustomRender listElementRender( children: [ (style?.listStylePosition ?? context.tree.style.listStylePosition) == - ListStylePosition.OUTSIDE + ListStylePosition.outside ? Padding( padding: style?.padding?.nonNegative ?? context.tree.style.padding?.nonNegative ?? @@ -185,15 +185,15 @@ CustomRender listElementRender( : 0.0), child: style?.markerContent ?? context.style.markerContent) - : Container(height: 0, width: 0), - Text("\u0020", + : const SizedBox(height: 0, width: 0), + const Text("\u0020", textAlign: TextAlign.right, style: TextStyle(fontWeight: FontWeight.w400)), Expanded( child: Padding( padding: (style?.listStylePosition ?? context.tree.style.listStylePosition) == - ListStylePosition.INSIDE + ListStylePosition.inside ? EdgeInsets.only( left: (style?.direction ?? context.tree.style.direction) != @@ -214,13 +214,13 @@ CustomRender listElementRender( ..insertAll( 0, context.tree.style.listStylePosition == - ListStylePosition.INSIDE + ListStylePosition.inside ? [ WidgetSpan( alignment: PlaceholderAlignment.middle, child: style?.markerContent ?? context.style.markerContent ?? - Container(height: 0, width: 0)) + const SizedBox(height: 0, width: 0)) ] : []), style: style ?? context.style, @@ -370,16 +370,16 @@ CustomRender networkImageRender({ if (!completer.isCompleted) { context.parser.cachedImageSizes[src] = size; completer.complete(size); - image.image.resolve(ImageConfiguration()).removeListener(listener!); + image.image.resolve(const ImageConfiguration()).removeListener(listener!); } }, onError: (object, stacktrace) { if (!completer.isCompleted) { completer.completeError(object); - image.image.resolve(ImageConfiguration()).removeListener(listener!); + image.image.resolve(const ImageConfiguration()).removeListener(listener!); } }); - image.image.resolve(ImageConfiguration()).addListener(listener); + image.image.resolve(const ImageConfiguration()).addListener(listener); } final attributes = context.tree.element!.attributes.cast(); @@ -487,12 +487,12 @@ CustomRender fallbackRender({Style? style, List? children}) => children: context.tree.children .expand((tree) => [ context.parser.parseTree(context, tree), - if (tree.style.display == Display.BLOCK && + if (tree.style.display == Display.block && tree.element?.parent?.localName != "th" && tree.element?.parent?.localName != "td" && tree.element?.localName != "html" && tree.element?.localName != "body") - TextSpan(text: "\n"), + const TextSpan(text: "\n"), ]) .toList(), )); @@ -516,8 +516,8 @@ Map generateDefaultRenders() { List _getListElementChildren( ListStylePosition? position, Function() buildChildren) { List children = buildChildren.call(); - if (position == ListStylePosition.INSIDE) { - final tabSpan = WidgetSpan( + if (position == ListStylePosition.inside) { + const tabSpan = WidgetSpan( child: Text("\t", textAlign: TextAlign.right, style: TextStyle(fontWeight: FontWeight.w400)), @@ -567,13 +567,13 @@ InlineSpan _getInteractableChildren(RenderContext context, } final _dataUriFormat = RegExp( - "^(?data):(?image\/[\\w\+\-\.]+)(?;base64)?\,(?.*)"); + "^(?data):(?image\\/[\\w\\+\\-\\.]+)(?;base64)?\\,(?.*)"); double _getVerticalOffset(StyledElement tree) { switch (tree.style.verticalAlign) { - case VerticalAlign.SUB: + case VerticalAlign.sub: return tree.style.fontSize!.value / 2.5; - case VerticalAlign.SUPER: + case VerticalAlign.sup: return tree.style.fontSize!.value / -2.5; default: return 0; @@ -618,5 +618,5 @@ double _aspectRatio( extension ClampedEdgeInsets on EdgeInsetsGeometry { EdgeInsetsGeometry get nonNegative => - this.clamp(EdgeInsets.zero, const EdgeInsets.all(double.infinity)); + clamp(EdgeInsets.zero, const EdgeInsets.all(double.infinity)); } diff --git a/lib/flutter_html.dart b/lib/flutter_html.dart index ec60476178..b702a2c24c 100644 --- a/lib/flutter_html.dart +++ b/lib/flutter_html.dart @@ -82,7 +82,7 @@ class Html extends StatefulWidget { this.style = const {}, }) : data = null, assert(document != null), - this.documentElement = document!.documentElement, + documentElement = document!.documentElement, _anchorKey = anchorKey ?? GlobalKey(), super(key: key); @@ -143,13 +143,13 @@ class Html extends StatefulWidget { /// An API that allows you to override the default style for any HTML element final Map style; - static List get tags => new List.from(STYLED_ELEMENTS) - ..addAll(INTERACTABLE_ELEMENTS) - ..addAll(REPLACED_ELEMENTS) - ..addAll(LAYOUT_ELEMENTS) - ..addAll(TABLE_CELL_ELEMENTS) - ..addAll(TABLE_DEFINITION_ELEMENTS) - ..addAll(EXTERNAL_ELEMENTS); + static List get tags => List.from(HtmlElements.styledElements) + ..addAll(HtmlElements.interactableElements) + ..addAll(HtmlElements.replacedElements) + ..addAll(HtmlElements.layoutElements) + ..addAll(HtmlElements.tableCellElements) + ..addAll(HtmlElements.tableDefinitionElements) + ..addAll(HtmlElements.externalElements); @override State createState() => _HtmlState(); @@ -263,7 +263,7 @@ class SelectableHtml extends StatefulWidget { this.scrollPhysics, }) : data = null, assert(document != null), - this.documentElement = document!.documentElement, + documentElement = document!.documentElement, _anchorKey = anchorKey ?? GlobalKey(), super(key: key); @@ -327,7 +327,7 @@ class SelectableHtml extends StatefulWidget { /// fallback to the default rendering. final Map customRenders; - static List get tags => new List.from(SELECTABLE_ELEMENTS); + static List get tags => List.from(HtmlElements.selectableElements); @override State createState() => _SelectableHtmlState(); @@ -346,7 +346,7 @@ class _SelectableHtmlState extends State { @override Widget build(BuildContext context) { - return Container( + return SizedBox( width: widget.shrinkWrap ? null : MediaQuery.of(context).size.width, child: HtmlParser( key: widget._anchorKey, diff --git a/lib/html_parser.dart b/lib/html_parser.dart index 7ed4e959af..74f253ef44 100644 --- a/lib/html_parser.dart +++ b/lib/html_parser.dart @@ -25,7 +25,6 @@ typedef OnCssParseError = String? Function( ); class HtmlParser extends StatelessWidget { - final Key? key; final dom.Element htmlData; final OnTap? onLinkTap; final OnTap? onAnchorTap; @@ -46,7 +45,7 @@ class HtmlParser extends StatelessWidget { final Map cachedImageSizes = {}; HtmlParser({ - required this.key, + required super.key, required this.htmlData, required this.onLinkTap, required this.onAnchorTap, @@ -61,12 +60,9 @@ class HtmlParser extends StatelessWidget { this.root, this.selectionControls, this.scrollPhysics, - }) : this.internalOnAnchorTap = onAnchorTap != null - ? onAnchorTap - : key != null + }) : internalOnAnchorTap = onAnchorTap ?? (key != null ? _handleAnchorTap(key, onLinkTap) - : onLinkTap, - super(key: key); + : onLinkTap); /// As the widget [build]s, the HTML data is processed into a tree of [StyledElement]s, /// which are then parsed into an [InlineSpan] tree that is then rendered to the screen by Flutter @@ -137,7 +133,7 @@ class HtmlParser extends StatelessWidget { style: Style.fromTextStyle(Theme.of(context).textTheme.bodyText2!), ); - html.nodes.forEach((node) { + for (var node in html.nodes) { tree.children.add(_recursiveLexer( node, customRenderMatchers, @@ -145,7 +141,7 @@ class HtmlParser extends StatelessWidget { context, parser, )); - }); + } return tree; } @@ -163,7 +159,7 @@ class HtmlParser extends StatelessWidget { ) { List children = []; - node.nodes.forEach((childNode) { + for (var childNode in node.nodes) { children.add(_recursiveLexer( childNode, customRenderMatchers, @@ -171,24 +167,24 @@ class HtmlParser extends StatelessWidget { context, parser, )); - }); + } //TODO(Sub6Resources): There's probably a more efficient way to look this up. if (node is dom.Element) { if (!tagsList.contains(node.localName)) { return EmptyContentElement(); } - if (STYLED_ELEMENTS.contains(node.localName)) { + if (HtmlElements.styledElements.contains(node.localName)) { return parseStyledElement(node, children); - } else if (INTERACTABLE_ELEMENTS.contains(node.localName)) { + } else if (HtmlElements.interactableElements.contains(node.localName)) { return parseInteractableElement(node, children); - } else if (REPLACED_ELEMENTS.contains(node.localName)) { + } else if (HtmlElements.replacedElements.contains(node.localName)) { return parseReplacedElement(node, children); - } else if (LAYOUT_ELEMENTS.contains(node.localName)) { + } else if (HtmlElements.layoutElements.contains(node.localName)) { return parseLayoutElement(node, children); - } else if (TABLE_CELL_ELEMENTS.contains(node.localName)) { + } else if (HtmlElements.tableCellElements.contains(node.localName)) { return parseTableCellElement(node, children); - } else if (TABLE_DEFINITION_ELEMENTS.contains(node.localName)) { + } else if (HtmlElements.tableDefinitionElements.contains(node.localName)) { return parseTableDefinitionElement(node, children); } else { final StyledElement tree = parseStyledElement(node, children); @@ -245,7 +241,9 @@ class HtmlParser extends StatelessWidget { } catch (_) {} }); - tree.children.forEach((e) => _applyExternalCss(declarations, e)); + for (var element in tree.children) { + _applyExternalCss(declarations, element); + } return tree; } @@ -259,7 +257,9 @@ class HtmlParser extends StatelessWidget { } } - tree.children.forEach((e) => _applyInlineStyles(e, errorHandler)); + for (var element in tree.children) { + _applyInlineStyles(element, errorHandler); + } return tree; } @@ -274,7 +274,9 @@ class HtmlParser extends StatelessWidget { } } catch (_) {} }); - tree.children.forEach((e) => _applyCustomStyles(style, e)); + for (var element in tree.children) { + _applyCustomStyles(style, element); + } return tree; } @@ -283,10 +285,11 @@ class HtmlParser extends StatelessWidget { /// child that doesn't specify a different style. static StyledElement _cascadeStyles( Map style, StyledElement tree) { - tree.children.forEach((child) { + + for (var child in tree.children) { child.style = tree.style.copyOnlyInherited(child.style); _cascadeStyles(style, child); - }); + } return tree; } @@ -342,11 +345,11 @@ class HtmlParser extends StatelessWidget { for (final entry in customRenders.keys) { if (entry.call(newContext)) { - final buildChildren = () => + buildChildren() => tree.children.map((tree) => parseTree(newContext, tree)).toList(); if (newContext.parser.selectable && customRenders[entry] is SelectableCustomRender) { - final selectableBuildChildren = () => tree.children + selectableBuildChildren() => tree.children .map((tree) => parseTree(newContext, tree) as TextSpan) .toList(); return (customRenders[entry] as SelectableCustomRender) @@ -367,14 +370,14 @@ class HtmlParser extends StatelessWidget { child: CssBoxWidget( style: tree.style, shrinkWrap: newContext.parser.shrinkWrap, - child: - customRenders[entry]!.widget!.call(newContext, buildChildren), childIsReplaced: true, //TODO is this true? + child: + customRenders[entry]!.widget!.call(newContext, buildChildren), ), ); } } - return WidgetSpan(child: Container(height: 0, width: 0)); + return const WidgetSpan(child: SizedBox(height: 0, width: 0)); } static OnTap _handleAnchorTap(Key key, OnTap? onLinkTap) => (String? url, @@ -398,7 +401,7 @@ class HtmlParser extends StatelessWidget { /// at https://www.w3.org/TR/css-text-3/ /// and summarized at https://medium.com/@patrickbrosset/when-does-white-space-matter-in-html-b90e8a7cdd33 static StyledElement _processInternalWhitespace(StyledElement tree) { - if ((tree.style.whiteSpace ?? WhiteSpace.NORMAL) == WhiteSpace.PRE) { + if ((tree.style.whiteSpace ?? WhiteSpace.normal) == WhiteSpace.pre) { // Preserve this whitespace } else if (tree is TextContentElement) { tree.text = _removeUnnecessaryWhitespace(tree.text!); @@ -479,7 +482,7 @@ class HtmlParser extends StatelessWidget { if (textIndex < 1 && tree.text!.startsWith(' ') && tree.element?.localName != "br" && - (!keepLeadingSpace.data || tree.style.display == Display.BLOCK) && + (!keepLeadingSpace.data || tree.style.display == Display.block) && (elementIndex < 1 || (elementIndex >= 1 && parentNodes?[elementIndex - 1] is dom.Text && @@ -505,8 +508,9 @@ class HtmlParser extends StatelessWidget { } } - tree.children - .forEach((e) => _processInlineWhitespaceRecursive(e, keepLeadingSpace)); + for (var element in tree.children) { + _processInlineWhitespaceRecursive(element, keepLeadingSpace); + } return tree; } @@ -520,8 +524,8 @@ class HtmlParser extends StatelessWidget { /// (4) Replace any instances of two or more spaces with a single space. static String _removeUnnecessaryWhitespace(String text) { return text - .replaceAll(RegExp("\ *(?=\n)"), "\n") - .replaceAll(RegExp("(?:\n)\ *"), "\n") + .replaceAll(RegExp("\\ *(?=\n)"), "\n") + .replaceAll(RegExp("(?:\n)\\ *"), "\n") .replaceAll("\n", " ") .replaceAll("\t", " ") .replaceAll(RegExp(" {2,}"), " "); @@ -540,17 +544,15 @@ class HtmlParser extends StatelessWidget { /// bullet all list items according to the [ListStyleType] they have been given. static StyledElement _processListCharactersRecursive( StyledElement tree, ListQueue olStack) { - if (tree.style.listStylePosition == null) { - tree.style.listStylePosition = ListStylePosition.OUTSIDE; - } + tree.style.listStylePosition ??= ListStylePosition.outside; if (tree.name == 'ol' && tree.style.listStyleType != null && tree.style.listStyleType!.type == "marker") { switch (tree.style.listStyleType!) { - case ListStyleType.LOWER_LATIN: - case ListStyleType.LOWER_ALPHA: - case ListStyleType.UPPER_LATIN: - case ListStyleType.UPPER_ALPHA: + case ListStyleType.lowerLatin: + case ListStyleType.lowerAlpha: + case ListStyleType.upperLatin: + case ListStyleType.upperAlpha: olStack.add(Context('a')); if ((tree.attributes['start'] != null ? int.tryParse(tree.attributes['start']!) @@ -571,30 +573,30 @@ class HtmlParser extends StatelessWidget { 1)); break; } - } else if (tree.style.display == Display.LIST_ITEM && + } else if (tree.style.display == Display.listItem && tree.style.listStyleType != null && tree.style.listStyleType!.type == "widget") { tree.style.markerContent = tree.style.listStyleType!.widget!; - } else if (tree.style.display == Display.LIST_ITEM && + } else if (tree.style.display == Display.listItem && tree.style.listStyleType != null && tree.style.listStyleType!.type == "image") { tree.style.markerContent = Image.network(tree.style.listStyleType!.text); - } else if (tree.style.display == Display.LIST_ITEM && + } else if (tree.style.display == Display.listItem && tree.style.listStyleType != null) { String marker = ""; switch (tree.style.listStyleType!) { - case ListStyleType.NONE: + case ListStyleType.none: break; - case ListStyleType.CIRCLE: + case ListStyleType.circle: marker = '○'; break; - case ListStyleType.SQUARE: + case ListStyleType.square: marker = '■'; break; - case ListStyleType.DISC: + case ListStyleType.disc: marker = '•'; break; - case ListStyleType.DECIMAL: + case ListStyleType.decimal: if (olStack.isEmpty) { olStack.add(Context((tree.attributes['start'] != null ? int.tryParse(tree.attributes['start'] ?? "") ?? 1 @@ -604,8 +606,8 @@ class HtmlParser extends StatelessWidget { olStack.last.data += 1; marker = '${olStack.last.data}.'; break; - case ListStyleType.LOWER_LATIN: - case ListStyleType.LOWER_ALPHA: + case ListStyleType.lowerLatin: + case ListStyleType.lowerAlpha: if (olStack.isEmpty) { olStack.add(Context('a')); if ((tree.attributes['start'] != null @@ -620,11 +622,11 @@ class HtmlParser extends StatelessWidget { } } } - marker = olStack.last.data.toString() + "."; + marker = "${olStack.last.data}."; olStack.last.data = olStack.last.data.toString().nextLetter(); break; - case ListStyleType.UPPER_LATIN: - case ListStyleType.UPPER_ALPHA: + case ListStyleType.upperLatin: + case ListStyleType.upperAlpha: if (olStack.isEmpty) { olStack.add(Context('a')); if ((tree.attributes['start'] != null @@ -639,10 +641,10 @@ class HtmlParser extends StatelessWidget { } } } - marker = olStack.last.data.toString().toUpperCase() + "."; + marker = "${olStack.last.data.toString().toUpperCase()}."; olStack.last.data = olStack.last.data.toString().nextLetter(); break; - case ListStyleType.LOWER_ROMAN: + case ListStyleType.lowerRoman: if (olStack.isEmpty) { olStack.add(Context((tree.attributes['start'] != null ? int.tryParse(tree.attributes['start'] ?? "") ?? 1 @@ -653,13 +655,12 @@ class HtmlParser extends StatelessWidget { if (olStack.last.data <= 0) { marker = '${olStack.last.data}.'; } else { - marker = (olStack.last.data as int) + marker = "${(olStack.last.data as int) .toRomanNumeralString()! - .toLowerCase() + - "."; + .toLowerCase()}."; } break; - case ListStyleType.UPPER_ROMAN: + case ListStyleType.upperRoman: if (olStack.isEmpty) { olStack.add(Context((tree.attributes['start'] != null ? int.tryParse(tree.attributes['start'] ?? "") ?? 1 @@ -670,7 +671,7 @@ class HtmlParser extends StatelessWidget { if (olStack.last.data <= 0) { marker = '${olStack.last.data}.'; } else { - marker = (olStack.last.data as int).toRomanNumeralString()! + "."; + marker = "${(olStack.last.data as int).toRomanNumeralString()!}."; } break; } @@ -681,7 +682,9 @@ class HtmlParser extends StatelessWidget { ); } - tree.children.forEach((e) => _processListCharactersRecursive(e, olStack)); + for (var element in tree.children) { + _processListCharactersRecursive(element, olStack); + } if (tree.name == 'ol') { olStack.removeLast(); @@ -700,7 +703,7 @@ class HtmlParser extends StatelessWidget { TextContentElement( text: tree.style.before, style: tree.style - .copyWith(beforeAfterNull: true, display: Display.INLINE), + .copyWith(beforeAfterNull: true, display: Display.inline), ), ); } @@ -708,7 +711,7 @@ class HtmlParser extends StatelessWidget { tree.children.add(TextContentElement( text: tree.style.after, style: - tree.style.copyWith(beforeAfterNull: true, display: Display.INLINE), + tree.style.copyWith(beforeAfterNull: true, display: Display.inline), )); } @@ -838,31 +841,31 @@ class HtmlParser extends StatelessWidget { ((tree.name == "body" && (index == 0 || index + 1 == tree.children.length || - tree.children[index - 1].style.display == Display.BLOCK || + tree.children[index - 1].style.display == Display.block || tree.children[index + 1].style.display == - Display.BLOCK)) || + Display.block)) || tree.name == "ul") && child.text!.replaceAll(' ', '').isEmpty) { toRemove.add(child); } else if (child is TextContentElement && child.text!.isEmpty && - child.style.whiteSpace != WhiteSpace.PRE) { + child.style.whiteSpace != WhiteSpace.pre) { toRemove.add(child); } else if (child is TextContentElement && - child.style.whiteSpace != WhiteSpace.PRE && - tree.style.display == Display.BLOCK && + child.style.whiteSpace != WhiteSpace.pre && + tree.style.display == Display.block && child.text!.isEmpty && lastChildBlock) { toRemove.add(child); - } else if (child.style.display == Display.NONE) { + } else if (child.style.display == Display.none) { toRemove.add(child); } else { _removeEmptyElements(child); } // This is used above to check if the previous element is a block element or a line break. - lastChildBlock = (child.style.display == Display.BLOCK || - child.style.display == Display.LIST_ITEM || + lastChildBlock = (child.style.display == Display.block || + child.style.display == Display.listItem || (child is TextContentElement && child.text == '\n')); }); tree.children.removeWhere((element) => toRemove.contains(element)); @@ -896,7 +899,7 @@ class HtmlParser extends StatelessWidget { final parentFontSize = tree.style.fontSize!.value; - tree.children.forEach((child) { + for (var child in tree.children) { if (child.style.fontSize == null) { child.style.fontSize = FontSize(parentFontSize); } else { @@ -928,7 +931,7 @@ class HtmlParser extends StatelessWidget { tree.style.setRelativeValues(remFontSize, emSize); _applyRelativeValuesRecursive(child, remFontSize, devicePixelRatio); - }); + } } } @@ -954,7 +957,7 @@ class RenderContext { extension IterateLetters on String { String nextLetter() { - String s = this.toLowerCase(); + String s = toLowerCase(); if (s == "z") { return String.fromCharCode(s.codeUnitAt(0) - 25) + String.fromCharCode(s.codeUnitAt(0) - 25); // AA or aa @@ -965,7 +968,7 @@ extension IterateLetters on String { // If a string of length > 1 ends in Z/z, // increment the string (excluding the last Z/z) recursively, // and append A/a (depending on casing) to it - return sub.nextLetter() + 'a'; + return '${sub.nextLetter()}a'; } else { // (take till last char) append with (increment last char) return sub + String.fromCharCode(lastChar.codeUnitAt(0) + 1); diff --git a/lib/src/css_box_widget.dart b/lib/src/css_box_widget.dart index 416e3fe11e..2dd59a52c1 100644 --- a/lib/src/css_box_widget.dart +++ b/lib/src/css_box_widget.dart @@ -5,18 +5,18 @@ import 'package:flutter/rendering.dart'; import 'package:flutter_html/flutter_html.dart'; class CssBoxWidget extends StatelessWidget { - CssBoxWidget({ - this.key, + const CssBoxWidget({ + super.key, required this.child, required this.style, this.textDirection, this.childIsReplaced = false, this.shrinkWrap = false, - }) : super(key: key); + }); /// Generates a CSSBoxWidget that contains a list of InlineSpan children. CssBoxWidget.withInlineSpanChildren({ - this.key, + super.key, required List children, required this.style, this.textDirection, @@ -25,18 +25,14 @@ class CssBoxWidget extends StatelessWidget { bool selectable = false, TextSelectionControls? selectionControls, ScrollPhysics? scrollPhysics, - }) : this.child = selectable + }) : child = selectable ? _generateSelectableWidgetChild( children, style, selectionControls, scrollPhysics, ) - : _generateWidgetChild(children, style), - super(key: key); - - /// An optional anchor key to use in finding this box - final AnchorKey? key; + : _generateWidgetChild(children, style); /// The child to be rendered within the CSS Box. final Widget child; @@ -67,7 +63,7 @@ class CssBoxWidget extends StatelessWidget { paddingSize: style.padding?.collapsedSize ?? Size.zero, borderSize: style.border?.dimensions.collapsedSize ?? Size.zero, margins: style.margin ?? Margins.zero, - display: style.display ?? Display.INLINE, + display: style.display ?? Display.inline, childIsReplaced: childIsReplaced, emValue: _calculateEmValue(style, context), textDirection: _checkTextDirection(context, textDirection), @@ -132,8 +128,8 @@ class CssBoxWidget extends StatelessWidget { /// width available to it or if it should just let its inner content /// determine the content-box's width. bool _shouldExpandToFillBlock() { - return (style.display == Display.BLOCK || - style.display == Display.LIST_ITEM) && + return (style.display == Display.block || + style.display == Display.listItem) && !childIsReplaced && !shrinkWrap; } @@ -244,7 +240,7 @@ class _CSSBoxRenderer extends MultiChildRenderObjectWidget { // and https://drafts.csswg.org/css2/#inline-replaced-width // and https://drafts.csswg.org/css2/#inlineblock-width // and https://drafts.csswg.org/css2/#inlineblock-replaced-width - if (display == Display.INLINE || display == Display.INLINE_BLOCK) { + if (display == Display.inline || display == Display.inlineBlock) { if (margins.left?.unit == Unit.auto) { leftMargin = Margin.zero(); } @@ -379,8 +375,9 @@ class _RenderCSSBox extends RenderBox @override void setupParentData(RenderBox child) { - if (child.parentData is! CSSBoxParentData) + if (child.parentData is! CSSBoxParentData) { child.parentData = CSSBoxParentData(); + } } static double getIntrinsicDimension(RenderBox? firstChild, @@ -453,13 +450,13 @@ class _RenderCSSBox extends RenderBox maxWidth: (this.width.unit != Unit.auto) ? this.width.value : containingBlockSize.width - - (this.margins.left?.value ?? 0) - - (this.margins.right?.value ?? 0), + (margins.left?.value ?? 0) - + (margins.right?.value ?? 0), maxHeight: (this.height.unit != Unit.auto) ? this.height.value : containingBlockSize.height - - (this.margins.top?.value ?? 0) - - (this.margins.bottom?.value ?? 0), + (margins.top?.value ?? 0) - + (margins.bottom?.value ?? 0), minWidth: (this.width.unit != Unit.auto) ? this.width.value : 0, minHeight: (this.height.unit != Unit.auto) ? this.height.value : 0, ); @@ -475,27 +472,27 @@ class _RenderCSSBox extends RenderBox //Calculate Width and Height of CSS Box height = childSize.height; switch (display) { - case Display.BLOCK: + case Display.block: width = (shrinkWrap || childIsReplaced) ? childSize.width + horizontalMargins : containingBlockSize.width; height = childSize.height + verticalMargins; break; - case Display.INLINE: + case Display.inline: width = childSize.width + horizontalMargins; height = childSize.height; break; - case Display.INLINE_BLOCK: + case Display.inlineBlock: width = childSize.width + horizontalMargins; height = childSize.height + verticalMargins; break; - case Display.LIST_ITEM: + case Display.listItem: width = shrinkWrap ? childSize.width + horizontalMargins : containingBlockSize.width; height = childSize.height + verticalMargins; break; - case Display.NONE: + case Display.none: width = 0; height = 0; break; @@ -528,22 +525,22 @@ class _RenderCSSBox extends RenderBox double leftOffset = 0; double topOffset = 0; switch (display) { - case Display.BLOCK: + case Display.block: leftOffset = leftMargin; topOffset = topMargin; break; - case Display.INLINE: + case Display.inline: leftOffset = leftMargin; break; - case Display.INLINE_BLOCK: + case Display.inlineBlock: leftOffset = leftMargin; topOffset = topMargin; break; - case Display.LIST_ITEM: + case Display.listItem: leftOffset = leftMargin; topOffset = topMargin; break; - case Display.NONE: + case Display.none: //No offset break; } @@ -568,7 +565,7 @@ class _RenderCSSBox extends RenderBox bool marginLeftIsAuto = marginLeft.unit == Unit.auto; bool marginRightIsAuto = marginRight.unit == Unit.auto; - if (display == Display.BLOCK) { + if (display == Display.block) { if (childIsReplaced) { widthIsAuto = false; } @@ -673,23 +670,18 @@ class _RenderCSSBox extends RenderBox void paint(PaintingContext context, Offset offset) { defaultPaint(context, offset); } - - @override - void dispose() { - super.dispose(); - } } extension Normalize on Dimension { void normalize(double emValue) { - switch (this.unit) { + switch (unit) { case Unit.rem: // Because CSSBoxWidget doesn't have any information about any // sort of tree structure, treat rem the same as em. The HtmlParser // widget handles rem/em values before they get to CSSBoxWidget. case Unit.em: - this.value *= emValue; - this.unit = Unit.px; + value *= emValue; + unit = Unit.px; return; case Unit.px: case Unit.auto: diff --git a/lib/src/css_parser.dart b/lib/src/css_parser.dart index 2d709187e9..92fb6a5dd3 100644 --- a/lib/src/css_parser.dart +++ b/lib/src/css_parser.dart @@ -8,7 +8,7 @@ import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_html/src/utils.dart'; Style declarationsToStyle(Map> declarations) { - Style style = new Style(); + Style style = Style(); declarations.forEach((property, value) { if (value.isNotEmpty) { switch (property) { @@ -27,11 +27,11 @@ Style declarationsToStyle(Map> declarations) { (element.text != "thin" && element.text != "medium" && element.text != "thick" && - !(element is css.LengthTerm) && - !(element is css.PercentageTerm) && - !(element is css.EmTerm) && - !(element is css.RemTerm) && - !(element is css.NumberTerm))); + element is! css.LengthTerm && + element is! css.PercentageTerm && + element is! css.EmTerm && + element is! css.RemTerm && + element is! css.NumberTerm)); List? borderColors = value .where((element) => ExpressionMapping.expressionToColor(element) != null) @@ -70,11 +70,11 @@ Style declarationsToStyle(Map> declarations) { (element.text != "thin" && element.text != "medium" && element.text != "thick" && - !(element is css.LengthTerm) && - !(element is css.PercentageTerm) && - !(element is css.EmTerm) && - !(element is css.RemTerm) && - !(element is css.NumberTerm))); + element is! css.LengthTerm && + element is! css.PercentageTerm && + element is! css.EmTerm && + element is! css.RemTerm && + element is! css.NumberTerm)); css.LiteralTerm? borderWidth = borderWidths.firstWhereOrNull((element) => element != null); css.Expression? borderColor = value.firstWhereOrNull((element) => @@ -128,11 +128,11 @@ Style declarationsToStyle(Map> declarations) { (element.text != "thin" && element.text != "medium" && element.text != "thick" && - !(element is css.LengthTerm) && - !(element is css.PercentageTerm) && - !(element is css.EmTerm) && - !(element is css.RemTerm) && - !(element is css.NumberTerm))); + element is! css.LengthTerm && + element is! css.PercentageTerm && + element is! css.EmTerm && + element is! css.RemTerm && + element is! css.NumberTerm)); css.LiteralTerm? borderWidth = borderWidths.firstWhereOrNull((element) => element != null); css.Expression? borderColor = value.firstWhereOrNull((element) => @@ -186,11 +186,11 @@ Style declarationsToStyle(Map> declarations) { (element.text != "thin" && element.text != "medium" && element.text != "thick" && - !(element is css.LengthTerm) && - !(element is css.PercentageTerm) && - !(element is css.EmTerm) && - !(element is css.RemTerm) && - !(element is css.NumberTerm))); + element is! css.LengthTerm && + element is! css.PercentageTerm && + element is! css.EmTerm && + element is! css.RemTerm && + element is! css.NumberTerm)); css.LiteralTerm? borderWidth = borderWidths.firstWhereOrNull((element) => element != null); css.Expression? borderColor = value.firstWhereOrNull((element) => @@ -244,11 +244,11 @@ Style declarationsToStyle(Map> declarations) { (element.text != "thin" && element.text != "medium" && element.text != "thick" && - !(element is css.LengthTerm) && - !(element is css.PercentageTerm) && - !(element is css.EmTerm) && - !(element is css.RemTerm) && - !(element is css.NumberTerm))); + element is! css.LengthTerm && + element is! css.PercentageTerm && + element is! css.EmTerm && + element is! css.RemTerm && + element is! css.NumberTerm)); css.LiteralTerm? borderWidth = borderWidths.firstWhereOrNull((element) => element != null); css.Expression? borderColor = value.firstWhereOrNull((element) => @@ -342,10 +342,10 @@ Style declarationsToStyle(Map> declarations) { if (position != null) { switch (position.text) { case 'outside': - style.listStylePosition = ListStylePosition.OUTSIDE; + style.listStylePosition = ListStylePosition.outside; break; case 'inside': - style.listStylePosition = ListStylePosition.INSIDE; + style.listStylePosition = ListStylePosition.inside; break; } } @@ -370,10 +370,10 @@ Style declarationsToStyle(Map> declarations) { if (value.first is css.LiteralTerm) { switch ((value.first as css.LiteralTerm).text) { case 'outside': - style.listStylePosition = ListStylePosition.OUTSIDE; + style.listStylePosition = ListStylePosition.outside; break; case 'inside': - style.listStylePosition = ListStylePosition.INSIDE; + style.listStylePosition = ListStylePosition.inside; break; } } @@ -395,10 +395,10 @@ Style declarationsToStyle(Map> declarations) { /// List might include other values than the ones we want for margin length, so make sure to remove those before passing it to [ExpressionMapping] marginLengths.removeWhere((element) => - !(element is css.LengthTerm) && - !(element is css.EmTerm) && - !(element is css.RemTerm) && - !(element is css.NumberTerm) && + element is! css.LengthTerm && + element is! css.EmTerm && + element is! css.RemTerm && + element is! css.NumberTerm && !(element.text == 'auto')); Margins margin = ExpressionMapping.expressionToMargins(marginLengths); style.margin = (style.margin ?? Margins.all(0)).copyWith( @@ -430,10 +430,10 @@ Style declarationsToStyle(Map> declarations) { /// List might include other values than the ones we want for padding length, so make sure to remove those before passing it to [ExpressionMapping] paddingLengths.removeWhere((element) => - !(element is css.LengthTerm) && - !(element is css.EmTerm) && - !(element is css.RemTerm) && - !(element is css.NumberTerm)); + element is! css.LengthTerm && + element is! css.EmTerm && + element is! css.RemTerm && + element is! css.NumberTerm); List padding = ExpressionMapping.expressionToPadding(paddingLengths); style.padding = (style.padding ?? EdgeInsets.zero).copyWith( @@ -494,14 +494,16 @@ Style declarationsToStyle(Map> declarations) { style.textDecoration = ExpressionMapping.expressionToTextDecorationLine( textDecorationList); - if (textDecorationColor != null) + if (textDecorationColor != null) { style.textDecorationColor = ExpressionMapping.expressionToColor(textDecorationColor) ?? style.textDecorationColor; - if (textDecorationStyle != null) + } + if (textDecorationStyle != null) { style.textDecorationStyle = ExpressionMapping.expressionToTextDecorationStyle( textDecorationStyle); + } break; case 'text-decoration-color': style.textDecorationColor = @@ -576,14 +578,14 @@ Map>> parseExternalCss( } class DeclarationVisitor extends css.Visitor { - Map>> _result = {}; - Map> _properties = {}; + final Map>> _result = {}; + final Map> _properties = {}; late String _selector; late String _currentProperty; Map>> getDeclarations( css.StyleSheet sheet) { - sheet.topLevels.forEach((element) { + for (var element in sheet.topLevels) { if (element.span != null) { _selector = element.span!.text; element.visit(this); @@ -591,18 +593,18 @@ class DeclarationVisitor extends css.Visitor { _properties.forEach((key, value) { if (_result[_selector]![key] != null) { _result[_selector]![key]! - .addAll(new List.from(value)); + .addAll(List.from(value)); } else { - _result[_selector]![key] = new List.from(value); + _result[_selector]![key] = List.from(value); } }); } else { _result[_selector] = - new Map>.from(_properties); + Map>.from(_properties); } _properties.clear(); } - }); + } return _result; } @@ -732,7 +734,7 @@ class ExpressionMapping { return double.tryParse(value.text) ?? 1.0; } else if (value is css.LengthTerm) { return double.tryParse( - value.text.replaceAll(new RegExp(r'\s+(\d+\.\d+)\s+'), '')) ?? + value.text.replaceAll(RegExp(r'\s+(\d+\.\d+)\s+'), '')) ?? 1.0; } else if (value is css.LiteralTerm) { switch (value.text) { @@ -787,18 +789,18 @@ class ExpressionMapping { if (value is css.LiteralTerm) { switch (value.text) { case 'block': - return Display.BLOCK; + return Display.block; case 'inline-block': - return Display.INLINE_BLOCK; + return Display.inlineBlock; case 'inline': - return Display.INLINE; + return Display.inline; case 'list-item': - return Display.LIST_ITEM; + return Display.listItem; case 'none': - return Display.NONE; + return Display.none; } } - return Display.INLINE; + return Display.inline; } static List expressionToFontFeatureSettings( @@ -844,7 +846,7 @@ class ExpressionMapping { // return FontSize.rem(double.tryParse(value.text) ?? 1, Unit.em); } else if (value is css.LengthTerm) { return FontSize(double.tryParse( - value.text.replaceAll(new RegExp(r'\s+(\d+\.\d+)\s+'), '')) ?? + value.text.replaceAll(RegExp(r'\s+(\d+\.\d+)\s+'), '')) ?? 16); } else if (value is css.LiteralTerm) { switch (value.text) { @@ -932,7 +934,7 @@ class ExpressionMapping { } else if (value is css.LengthTerm) { return LineHeight( double.tryParse( - value.text.replaceAll(new RegExp(r'\s+(\d+\.\d+)\s+'), '')), + value.text.replaceAll(RegExp(r'\s+(\d+\.\d+)\s+'), '')), units: "length"); } return LineHeight.normal; @@ -944,27 +946,27 @@ class ExpressionMapping { } switch (value.text) { case 'disc': - return ListStyleType.DISC; + return ListStyleType.disc; case 'circle': - return ListStyleType.CIRCLE; + return ListStyleType.circle; case 'decimal': - return ListStyleType.DECIMAL; + return ListStyleType.decimal; case 'lower-alpha': - return ListStyleType.LOWER_ALPHA; + return ListStyleType.lowerAlpha; case 'lower-latin': - return ListStyleType.LOWER_LATIN; + return ListStyleType.lowerLatin; case 'lower-roman': - return ListStyleType.LOWER_ROMAN; + return ListStyleType.lowerRoman; case 'square': - return ListStyleType.SQUARE; + return ListStyleType.square; case 'upper-alpha': - return ListStyleType.UPPER_ALPHA; + return ListStyleType.upperAlpha; case 'upper-latin': - return ListStyleType.UPPER_LATIN; + return ListStyleType.upperLatin; case 'upper-roman': - return ListStyleType.UPPER_ROMAN; + return ListStyleType.upperRoman; case 'none': - return ListStyleType.NONE; + return ListStyleType.none; } return null; } @@ -1067,7 +1069,7 @@ class ExpressionMapping { return double.tryParse(value.text); } else if (value is css.LengthTerm) { return double.tryParse( - value.text.replaceAll(new RegExp(r'\s+(\d+\.\d+)\s+'), '')); + value.text.replaceAll(RegExp(r'\s+(\d+\.\d+)\s+'), '')); } return null; } @@ -1082,7 +1084,7 @@ class ExpressionMapping { // TODO there are several other available terms processed by the CSS parser } else if (value is css.LengthTerm) { double number = double.parse( - value.text.replaceAll(new RegExp(r'\s+(\d+\.\d+)\s+'), '')); + value.text.replaceAll(RegExp(r'\s+(\d+\.\d+)\s+'), '')); Unit unit = _unitMap(value.unit); return LengthOrPercent(number, unit); } @@ -1139,8 +1141,9 @@ class ExpressionMapping { } } } - if (decorationList.contains(TextDecoration.none)) + if (decorationList.contains(TextDecoration.none)) { decorationList = [TextDecoration.none]; + } return TextDecoration.combine(decorationList); } @@ -1181,7 +1184,7 @@ class ExpressionMapping { css.Expression? blurRadius; css.Expression? color; int expressionIndex = 0; - list.forEach((element) { + for (var element in list) { if (element is css.HexColorTerm || element is css.FunctionTerm) { color = element; } else if (expressionIndex == 0) { @@ -1193,7 +1196,7 @@ class ExpressionMapping { } else { blurRadius = element; } - }); + } RegExp nonNumberRegex = RegExp(r'\s+(\d+\.\d+)\s+'); if (offsetX is css.LiteralTerm && offsetY is css.LiteralTerm) { if (color != null && @@ -1201,14 +1204,14 @@ class ExpressionMapping { shadow.add(Shadow( color: expressionToColor(color)!, offset: Offset( - double.tryParse((offsetX as css.LiteralTerm) + double.tryParse((offsetX) .text .replaceAll(nonNumberRegex, ''))!, - double.tryParse((offsetY as css.LiteralTerm) + double.tryParse((offsetY) .text .replaceAll(nonNumberRegex, ''))!), blurRadius: (blurRadius is css.LiteralTerm) - ? double.tryParse((blurRadius as css.LiteralTerm) + ? double.tryParse((blurRadius) .text .replaceAll(nonNumberRegex, ''))! : 0.0, @@ -1216,14 +1219,14 @@ class ExpressionMapping { } else { shadow.add(Shadow( offset: Offset( - double.tryParse((offsetX as css.LiteralTerm) + double.tryParse((offsetX) .text .replaceAll(nonNumberRegex, ''))!, - double.tryParse((offsetY as css.LiteralTerm) + double.tryParse((offsetY) .text .replaceAll(nonNumberRegex, ''))!), blurRadius: (blurRadius is css.LiteralTerm) - ? double.tryParse((blurRadius as css.LiteralTerm) + ? double.tryParse((blurRadius) .text .replaceAll(nonNumberRegex, ''))! : 0.0, @@ -1235,17 +1238,18 @@ class ExpressionMapping { return finalShadows; } - static Color stringToColor(String _text) { - var text = _text.replaceFirst('#', ''); - if (text.length == 3) + static Color stringToColor(String rawText) { + var text = rawText.replaceFirst('#', ''); + if (text.length == 3) { text = text.replaceAllMapped(RegExp(r"[a-f]|\d", caseSensitive: false), (match) => '${match.group(0)}${match.group(0)}'); + } if (text.length > 6) { - text = "0x" + text; + text = "0x$text"; } else { - text = "0xFF" + text; + text = "0xFF$text"; } - return new Color(int.parse(text)); + return Color(int.parse(text)); } static Color? rgbOrRgbaToColor(String text) { @@ -1278,7 +1282,7 @@ class ExpressionMapping { final hslText = text.replaceAll(')', '').replaceAll(' ', ''); final hslValues = hslText.split(',').toList(); List parsedHsl = []; - hslValues.forEach((element) { + for (var element in hslValues) { if (element.contains("%") && double.tryParse(element.replaceAll("%", "")) != null) { parsedHsl.add(double.tryParse(element.replaceAll("%", ""))! * 0.01); @@ -1291,7 +1295,7 @@ class ExpressionMapping { parsedHsl.add(double.tryParse(element)); } } - }); + } if (parsedHsl.length == 4 && !parsedHsl.contains(null)) { return HSLColor.fromAHSL( parsedHsl.last!, parsedHsl.first!, parsedHsl[1]!, parsedHsl[2]!) @@ -1300,8 +1304,9 @@ class ExpressionMapping { return HSLColor.fromAHSL( 1.0, parsedHsl.first!, parsedHsl[1]!, parsedHsl.last!) .toColor(); - } else + } else { return Colors.black; + } } static Color? namedColorToColor(String text) { @@ -1310,7 +1315,8 @@ class ExpressionMapping { orElse: () => ""); if (namedColor != "") { return stringToColor(namedColors[namedColor]!); - } else + } else { return null; + } } } diff --git a/lib/src/html_elements.dart b/lib/src/html_elements.dart index bf4e363024..6e2bccb37c 100644 --- a/lib/src/html_elements.dart +++ b/lib/src/html_elements.dart @@ -2,292 +2,208 @@ export 'styled_element.dart'; export 'interactable_element.dart'; export 'replaced_element.dart'; -const STYLED_ELEMENTS = [ - "abbr", - "acronym", - "address", - "b", - "bdi", - "bdo", - "big", - "cite", - "code", - "data", - "del", - "dfn", - "em", - "font", - "i", - "ins", - "kbd", - "mark", - "q", - "rt", - "s", - "samp", - "small", - "span", - "strike", - "strong", - "sub", - "sup", - "time", - "tt", - "u", - "var", - "wbr", +class HtmlElements { - //BLOCK ELEMENTS - "article", - "aside", - "blockquote", - "body", - "center", - "dd", - "div", - "dl", - "dt", - "figcaption", - "figure", - "footer", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "header", - "hr", - "html", - "li", - "main", - "nav", - "noscript", - "ol", - "p", - "pre", - "section", - "summary", - "ul", -]; + static const styledElements = [ + "abbr", + "acronym", + "address", + "b", + "bdi", + "bdo", + "big", + "cite", + "code", + "data", + "del", + "dfn", + "em", + "font", + "i", + "ins", + "kbd", + "mark", + "q", + "rt", + "s", + "samp", + "small", + "span", + "strike", + "strong", + "sub", + "sup", + "time", + "tt", + "u", + "var", + "wbr", -const BLOCK_ELEMENTS = [ - "article", - "aside", - "blockquote", - "body", - "center", - "dd", - "div", - "dl", - "dt", - "figcaption", - "figure", - "footer", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "header", - "hr", - "html", - "li", - "main", - "nav", - "noscript", - "ol", - "p", - "pre", - "section", - "summary", - "ul", -]; + //BLOCK ELEMENTS + "article", + "aside", + "blockquote", + "body", + "center", + "dd", + "div", + "dl", + "dt", + "figcaption", + "figure", + "footer", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "header", + "hr", + "html", + "li", + "main", + "nav", + "noscript", + "ol", + "p", + "pre", + "section", + "summary", + "ul", + ]; -const INTERACTABLE_ELEMENTS = [ - "a", -]; + static const blockElements = [ + "article", + "aside", + "blockquote", + "body", + "center", + "dd", + "div", + "dl", + "dt", + "figcaption", + "figure", + "footer", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "header", + "hr", + "html", + "li", + "main", + "nav", + "noscript", + "ol", + "p", + "pre", + "section", + "summary", + "ul", + ]; -const REPLACED_ELEMENTS = [ - "br", - "template", - "rp", - "rt", - "ruby", -]; + static const interactableElements = [ + "a", + ]; -const LAYOUT_ELEMENTS = [ - "details", - "tr", - "tbody", - "tfoot", - "thead", -]; + static const replacedElements = [ + "br", + "template", + "rp", + "rt", + "ruby", + ]; -const TABLE_CELL_ELEMENTS = ["th", "td"]; + static const layoutElements = [ + "details", + "tr", + "tbody", + "tfoot", + "thead", + ]; -const TABLE_DEFINITION_ELEMENTS = ["col", "colgroup"]; + static const tableCellElements = ["th", "td"]; -const EXTERNAL_ELEMENTS = [ - "audio", - "iframe", - "img", - "math", - "svg", - "table", - "video" -]; + static const tableDefinitionElements = ["col", "colgroup"]; -const REPLACED_EXTERNAL_ELEMENTS = ["iframe", "img", "video", "audio"]; + static const externalElements = [ + "audio", + "iframe", + "img", + "math", + "svg", + "table", + "video" + ]; -const SELECTABLE_ELEMENTS = [ - "br", - "a", - "article", - "aside", - "blockquote", - "body", - "center", - "dd", - "div", - "dl", - "dt", - "figcaption", - "figure", - "footer", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "header", - "hr", - "html", - "main", - "nav", - "noscript", - "p", - "pre", - "section", - "summary", - "abbr", - "acronym", - "address", - "b", - "bdi", - "bdo", - "big", - "cite", - "code", - "data", - "del", - "dfn", - "em", - "font", - "i", - "ins", - "kbd", - "mark", - "q", - "s", - "samp", - "small", - "span", - "strike", - "strong", - "time", - "tt", - "u", - "var", - "wbr", -]; + static const replacedExternalElements = ["iframe", "img", "video", "audio"]; -/** - Here is a list of elements with planned support: - a - i [x] - abbr - s [x] - acronym - s [x] - address - s [x] - audio - c [x] - article - b [x] - aside - b [x] - b - s [x] - bdi - s [x] - bdo - s [x] - big - s [x] - blockquote- b [x] - body - b [x] - br - b [x] - button - i [ ] - caption - b [ ] - center - b [x] - cite - s [x] - code - s [x] - data - s [x] - dd - b [x] - del - s [x] - dfn - s [x] - div - b [x] - dl - b [x] - dt - b [x] - em - s [x] - figcaption- b [x] - figure - b [x] - font - s [x] - footer - b [x] - h1 - b [x] - h2 - b [x] - h3 - b [x] - h4 - b [x] - h5 - b [x] - h6 - b [x] - head - e [x] - header - b [x] - hr - b [x] - html - b [x] - i - s [x] - img - c [x] - ins - s [x] - kbd - s [x] - li - b [x] - main - b [x] - mark - s [x] - nav - b [x] - noscript - b [x] - ol - b [x] post - p - b [x] - pre - b [x] - q - s [x] post - rp - s [x] - rt - s [x] - ruby - s [x] - s - s [x] - samp - s [x] - section - b [x] - small - s [x] - source - [-] child of content - span - s [x] - strike - s [x] - strong - s [x] - sub - s [x] - sup - s [x] - svg - c [x] - table - b [x] - tbody - b [x] - td - s [ ] - template - e [x] - tfoot - b [x] - th - s [ ] - thead - b [x] - time - s [x] - tr - ? [ ] - track - [-] child of content - tt - s [x] - u - s [x] - ul - b [x] post - var - s [x] - video - c [x] - wbr - s [x] - */ + static const selectableElements = [ + "br", + "a", + "article", + "aside", + "blockquote", + "body", + "center", + "dd", + "div", + "dl", + "dt", + "figcaption", + "figure", + "footer", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "header", + "hr", + "html", + "main", + "nav", + "noscript", + "p", + "pre", + "section", + "summary", + "abbr", + "acronym", + "address", + "b", + "bdi", + "bdo", + "big", + "cite", + "code", + "data", + "del", + "dfn", + "em", + "font", + "i", + "ins", + "kbd", + "mark", + "q", + "s", + "samp", + "small", + "span", + "strike", + "strong", + "time", + "tt", + "u", + "var", + "wbr", + ]; + +} \ No newline at end of file diff --git a/lib/src/interactable_element.dart b/lib/src/interactable_element.dart index 57a918fdbe..6a1bf90a4f 100644 --- a/lib/src/interactable_element.dart +++ b/lib/src/interactable_element.dart @@ -19,7 +19,7 @@ class InteractableElement extends StyledElement { /// A [Gesture] indicates the type of interaction by a user. enum Gesture { - TAP, + tap, } StyledElement parseInteractableElement( diff --git a/lib/src/layout_element.dart b/lib/src/layout_element.dart index d366ce1fc3..d5ac7e8cb4 100644 --- a/lib/src/layout_element.dart +++ b/lib/src/layout_element.dart @@ -29,7 +29,7 @@ class TableSectionLayoutElement extends LayoutElement { @override Widget toWidget(RenderContext context) { // Not rendered; TableLayoutElement will instead consume its children - return Container(child: Text("TABLE SECTION")); + return const Text("TABLE SECTION"); } } @@ -43,7 +43,7 @@ class TableRowLayoutElement extends LayoutElement { @override Widget toWidget(RenderContext context) { // Not rendered; TableLayoutElement will instead consume its children - return Container(child: Text("TABLE ROW")); + return const Text("TABLE ROW"); } } @@ -158,7 +158,7 @@ class DetailsContentElement extends LayoutElement { children: firstChild == null ? [] : [firstChild], style: style, ) - : Text("Details"), + : const Text("Details"), children: [ CssBoxWidget.withInlineSpanChildren( children: getChildren( @@ -188,7 +188,7 @@ class EmptyLayoutElement extends LayoutElement { ); @override - Widget? toWidget(_) => null; + Widget? toWidget(context) => null; } LayoutElement parseLayoutElement( diff --git a/lib/src/replaced_element.dart b/lib/src/replaced_element.dart index 3234da0f1d..28d95b7213 100644 --- a/lib/src/replaced_element.dart +++ b/lib/src/replaced_element.dart @@ -59,7 +59,7 @@ class TextContentElement extends ReplacedElement { } @override - Widget? toWidget(_) => null; + Widget? toWidget(context) => null; } class EmptyContentElement extends ReplacedElement { @@ -67,10 +67,12 @@ class EmptyContentElement extends ReplacedElement { : super(name: name, style: Style(), elementId: "[[No ID]]"); @override - Widget? toWidget(_) => null; + Widget? toWidget(context) => null; } class RubyElement extends ReplacedElement { + + @override dom.Element element; RubyElement({ @@ -97,12 +99,12 @@ class RubyElement extends ReplacedElement { (element.text ?? "").trim().isEmpty && index > 0 && index + 1 < context.tree.children.length && - !(context.tree.children[index - 1] is TextContentElement) && - !(context.tree.children[index + 1] is TextContentElement))) { + context.tree.children[index - 1] is! TextContentElement && + context.tree.children[index + 1] is! TextContentElement)) { children.add(element); } }); - children.forEach((c) { + for (var c in children) { if (c.name == "rt" && node != null) { final widget = Stack( alignment: Alignment.center, @@ -128,10 +130,10 @@ class RubyElement extends ReplacedElement { style: context.style, child: node is TextContentElement ? Text( - (node as TextContentElement).text?.trim() ?? "", + node.text?.trim() ?? "", style: context.style.generateTextStyle(), ) - : RichText(text: context.parser.parseTree(context, node!)), + : RichText(text: context.parser.parseTree(context, node)), ), ], ); @@ -139,7 +141,7 @@ class RubyElement extends ReplacedElement { } else { node = c; } - }); + } return Padding( padding: EdgeInsets.only(top: rubySize), child: Wrap( @@ -166,7 +168,7 @@ ReplacedElement parseReplacedElement( case "br": return TextContentElement( text: "\n", - style: Style(whiteSpace: WhiteSpace.PRE), + style: Style(whiteSpace: WhiteSpace.pre), element: element, node: element, ); diff --git a/lib/src/style/fontsize.dart b/lib/src/style/fontsize.dart index 5d766c630a..d3ed2c13ef 100644 --- a/lib/src/style/fontsize.dart +++ b/lib/src/style/fontsize.dart @@ -1,5 +1,3 @@ -//TODO implement dimensionality - import 'length.dart'; class FontSize extends LengthOrPercent { @@ -35,5 +33,5 @@ class FontSize extends LengthOrPercent { return parent; } - double get emValue => this.value; + double get emValue => value; } diff --git a/lib/src/style/length.dart b/lib/src/style/length.dart index 3fab69263d..f9eae57293 100644 --- a/lib/src/style/length.dart +++ b/lib/src/style/length.dart @@ -1,16 +1,16 @@ /// These are the base unit types -enum _UnitType { +enum UnitType { percent, length, auto, - lengthPercent(children: [_UnitType.length, _UnitType.percent]), - lengthPercentAuto(children: [_UnitType.length, _UnitType.percent, _UnitType.auto]); + lengthPercent(children: [UnitType.length, UnitType.percent]), + lengthPercentAuto(children: [UnitType.length, UnitType.percent, UnitType.auto]); - final List<_UnitType> children; + final List children; - const _UnitType({this.children = const []}); + const UnitType({this.children = const []}); - bool matches(_UnitType other) { + bool matches(UnitType other) { return this == other || children.contains(other); } } @@ -18,18 +18,18 @@ enum _UnitType { /// A Unit represents a CSS unit enum Unit { //ch, - em(_UnitType.length), + em(UnitType.length), //ex, - percent(_UnitType.percent), - px(_UnitType.length), - rem(_UnitType.length), + percent(UnitType.percent), + px(UnitType.length), + rem(UnitType.length), //Q, //vh, //vw, - auto(_UnitType.auto); + auto(UnitType.auto); const Unit(this.unitType); - final _UnitType unitType; + final UnitType unitType; } /// Represents a CSS dimension https://drafts.csswg.org/css-values/#dimensions @@ -37,15 +37,15 @@ abstract class Dimension { double value; Unit unit; - Dimension(this.value, this.unit, _UnitType _dimensionUnitType) - : assert(_dimensionUnitType.matches(unit.unitType), + Dimension(this.value, this.unit, UnitType dimensionUnitType) + : assert(dimensionUnitType.matches(unit.unitType), "This Dimension was given a Unit that isn't specified."); } /// This dimension takes a value with a length unit such as px or em. Note that /// these can be fixed or relative (but they must not be a percent) class Length extends Dimension { - Length(double value, [Unit unit = Unit.px]) : super(value, unit, _UnitType.length); + Length(double value, [Unit unit = Unit.px]) : super(value, unit, UnitType.length); } /// This dimension takes a value with a length-percent unit such as px or em @@ -53,10 +53,10 @@ class Length extends Dimension { /// percent) class LengthOrPercent extends Dimension { LengthOrPercent(double value, [Unit unit = Unit.px]) - : super(value, unit, _UnitType.lengthPercent); + : super(value, unit, UnitType.lengthPercent); } class AutoOrLengthOrPercent extends Dimension { AutoOrLengthOrPercent(double value, [Unit unit = Unit.px]) - : super(value, unit, _UnitType.lengthPercentAuto); + : super(value, unit, UnitType.lengthPercentAuto); } diff --git a/lib/src/styled_element.dart b/lib/src/styled_element.dart index 8434544c50..87625b2d98 100644 --- a/lib/src/styled_element.dart +++ b/lib/src/styled_element.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/src/css_parser.dart'; -import 'package:flutter_html/src/style/margin.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. @@ -23,7 +22,7 @@ class StyledElement { required this.children, required this.style, required dom.Element? node, - }) : this._node = node; + }) : _node = node; bool matchesSelector(String selector) => (_node != null && matches(_node!, selector)) || name == selector; @@ -32,7 +31,7 @@ class StyledElement { _node?.attributes.map((key, value) { return MapEntry(key.toString(), value); }) ?? - Map(); + {}; dom.Element? get element => _node; @@ -40,10 +39,10 @@ class StyledElement { String toString() { String selfData = "[$name] ${children.length} ${elementClasses.isNotEmpty == true ? 'C:${elementClasses.toString()}' : ''}${elementId.isNotEmpty == true ? 'ID: $elementId' : ''}"; - children.forEach((child) { + for (var child in children) { selfData += ("\n${child.toString()}") .replaceAll(RegExp("^", multiLine: true), "-"); - }); + } return selfData; } } @@ -73,12 +72,12 @@ StyledElement parseStyledElement( continue italics; case "article": styledElement.style = Style( - display: Display.BLOCK, + display: Display.block, ); break; case "aside": styledElement.style = Style( - display: Display.BLOCK, + display: Display.block, ); break; bold: @@ -106,25 +105,25 @@ StyledElement parseStyledElement( if (element.parent!.localName == "blockquote") { styledElement.style = Style( margin: Margins.only(left: 40.0, right: 40.0, bottom: 14.0), - display: Display.BLOCK, + display: Display.block, ); } else { styledElement.style = Style( margin: Margins.symmetric(horizontal: 40.0, vertical: 14.0), - display: Display.BLOCK, + display: Display.block, ); } break; case "body": styledElement.style = Style( margin: Margins.all(8.0), - display: Display.BLOCK, + display: Display.block, ); break; case "center": styledElement.style = Style( alignment: Alignment.center, - display: Display.BLOCK, + display: Display.block, ); break; case "cite": @@ -138,7 +137,7 @@ StyledElement parseStyledElement( case "dd": styledElement.style = Style( margin: Margins.only(left: 40.0), - display: Display.BLOCK, + display: Display.block, ); break; strikeThrough: @@ -152,36 +151,36 @@ StyledElement parseStyledElement( case "div": styledElement.style = Style( margin: Margins.all(0), - display: Display.BLOCK, + display: Display.block, ); break; case "dl": styledElement.style = Style( margin: Margins.symmetric(vertical: 14.0), - display: Display.BLOCK, + display: Display.block, ); break; case "dt": styledElement.style = Style( - display: Display.BLOCK, + display: Display.block, ); break; case "em": continue italics; case "figcaption": styledElement.style = Style( - display: Display.BLOCK, + display: Display.block, ); break; case "figure": styledElement.style = Style( margin: Margins.symmetric(vertical: 14.0, horizontal: 40.0), - display: Display.BLOCK, + display: Display.block, ); break; case "footer": styledElement.style = Style( - display: Display.BLOCK, + display: Display.block, ); break; case "font": @@ -203,7 +202,7 @@ StyledElement parseStyledElement( fontSize: FontSize(2, Unit.em), fontWeight: FontWeight.bold, margin: Margins.symmetric(vertical: 0.67, unit: Unit.em), - display: Display.BLOCK, + display: Display.block, ); break; case "h2": @@ -211,7 +210,7 @@ StyledElement parseStyledElement( fontSize: FontSize(1.5, Unit.em), fontWeight: FontWeight.bold, margin: Margins.symmetric(vertical: 0.83, unit: Unit.em), - display: Display.BLOCK, + display: Display.block, ); break; case "h3": @@ -219,14 +218,14 @@ StyledElement parseStyledElement( fontSize: FontSize(1.17, Unit.em), fontWeight: FontWeight.bold, margin: Margins.symmetric(vertical: 1, unit: Unit.em), - display: Display.BLOCK, + display: Display.block, ); break; case "h4": styledElement.style = Style( fontWeight: FontWeight.bold, margin: Margins.symmetric(vertical: 1.33, unit: Unit.em), - display: Display.BLOCK, + display: Display.block, ); break; case "h5": @@ -234,7 +233,7 @@ StyledElement parseStyledElement( fontSize: FontSize(0.83, Unit.em), fontWeight: FontWeight.bold, margin: Margins.symmetric(vertical: 1.67, unit: Unit.em), - display: Display.BLOCK, + display: Display.block, ); break; case "h6": @@ -242,12 +241,12 @@ StyledElement parseStyledElement( fontSize: FontSize(0.67, Unit.em), fontWeight: FontWeight.bold, margin: Margins.symmetric(vertical: 2.33, unit: Unit.em), - display: Display.BLOCK, + display: Display.block, ); break; case "header": styledElement.style = Style( - display: Display.BLOCK, + display: Display.block, ); break; case "hr": @@ -259,12 +258,12 @@ StyledElement parseStyledElement( right: Margin.auto(), ), border: Border.all(), - display: Display.BLOCK, + display: Display.block, ); break; case "html": styledElement.style = Style( - display: Display.BLOCK, + display: Display.block, ); break; italics: @@ -279,12 +278,12 @@ StyledElement parseStyledElement( continue monospace; case "li": styledElement.style = Style( - display: Display.LIST_ITEM, + display: Display.listItem, ); break; case "main": styledElement.style = Style( - display: Display.BLOCK, + display: Display.block, ); break; case "mark": @@ -295,12 +294,12 @@ StyledElement parseStyledElement( break; case "nav": styledElement.style = Style( - display: Display.BLOCK, + display: Display.block, ); break; case "noscript": styledElement.style = Style( - display: Display.BLOCK, + display: Display.block, ); break; case "ol": @@ -309,33 +308,33 @@ StyledElement parseStyledElement( if (element.parent!.localName == "li") { styledElement.style = Style( // margin: EdgeInsets.only(left: 30.0), - display: Display.BLOCK, + display: Display.block, listStyleType: element.localName == "ol" - ? ListStyleType.DECIMAL - : ListStyleType.DISC, + ? ListStyleType.decimal + : ListStyleType.disc, ); } else { styledElement.style = Style( // margin: EdgeInsets.only(left: 30.0, top: 14.0, bottom: 14.0), - display: Display.BLOCK, + display: Display.block, listStyleType: element.localName == "ol" - ? ListStyleType.DECIMAL - : ListStyleType.DISC, + ? ListStyleType.decimal + : ListStyleType.disc, ); } break; case "p": styledElement.style = Style( margin: Margins.symmetric(vertical: 1, unit: Unit.em), - display: Display.BLOCK, + display: Display.block, ); break; case "pre": styledElement.style = Style( fontFamily: 'monospace', margin: Margins.symmetric(vertical: 14.0), - whiteSpace: WhiteSpace.PRE, - display: Display.BLOCK, + whiteSpace: WhiteSpace.pre, + display: Display.block, ); break; case "q": @@ -350,7 +349,7 @@ StyledElement parseStyledElement( continue monospace; case "section": styledElement.style = Style( - display: Display.BLOCK, + display: Display.block, ); break; case "small": @@ -365,13 +364,13 @@ StyledElement parseStyledElement( case "sub": styledElement.style = Style( fontSize: FontSize.smaller, - verticalAlign: VerticalAlign.SUB, + verticalAlign: VerticalAlign.sub, ); break; case "sup": styledElement.style = Style( fontSize: FontSize.smaller, - verticalAlign: VerticalAlign.SUPER, + verticalAlign: VerticalAlign.sup, ); break; case "tt": diff --git a/lib/style.dart b/lib/style.dart index 38d12ca136..d9993d1ba5 100644 --- a/lib/style.dart +++ b/lib/style.dart @@ -243,9 +243,9 @@ class Style { this.textOverflow, this.textTransform = TextTransform.none, }) { - if (this.alignment == null && - (display == Display.BLOCK || display == Display.LIST_ITEM)) { - this.alignment = Alignment.centerLeft; + if (alignment == null && + (display == Display.block || display == Display.listItem)) { + alignment = Alignment.centerLeft; } } @@ -358,7 +358,7 @@ class Style { : backgroundColor, color: child.color ?? color, direction: child.direction ?? direction, - display: display == Display.NONE ? display : child.display, + display: display == Display.none ? display : child.display, fontFamily: child.fontFamily ?? fontFamily, fontFamilyFallback: child.fontFamilyFallback ?? fontFamilyFallback, fontFeatureSettings: child.fontFeatureSettings ?? fontFeatureSettings, @@ -462,24 +462,24 @@ class Style { } Style.fromTextStyle(TextStyle textStyle) { - 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.fontFamilyFallback = textStyle.fontFamilyFallback; - this.fontFeatureSettings = textStyle.fontFeatures; - this.fontSize = + backgroundColor = textStyle.backgroundColor; + color = textStyle.color; + textDecoration = textStyle.decoration; + textDecorationColor = textStyle.decorationColor; + textDecorationStyle = textStyle.decorationStyle; + textDecorationThickness = textStyle.decorationThickness; + fontFamily = textStyle.fontFamily; + fontFamilyFallback = textStyle.fontFamilyFallback; + fontFeatureSettings = textStyle.fontFeatures; + fontSize = textStyle.fontSize != null ? FontSize(textStyle.fontSize!) : null; - this.fontStyle = textStyle.fontStyle; - this.fontWeight = textStyle.fontWeight; - this.letterSpacing = textStyle.letterSpacing; - this.textShadow = textStyle.shadows; - this.wordSpacing = textStyle.wordSpacing; - this.lineHeight = LineHeight(textStyle.height ?? 1.2); - this.textTransform = TextTransform.none; + fontStyle = textStyle.fontStyle; + fontWeight = textStyle.fontWeight; + letterSpacing = textStyle.letterSpacing; + textShadow = textStyle.shadows; + wordSpacing = textStyle.wordSpacing; + lineHeight = LineHeight(textStyle.height ?? 1.2); + textTransform = TextTransform.none; } /// Sets any dimensions set to rem or em to the computed size @@ -541,11 +541,11 @@ class Style { } enum Display { - BLOCK, - INLINE, - INLINE_BLOCK, - LIST_ITEM, - NONE, + block, + inline, + inlineBlock, + listItem, + none, } class ListStyleType { @@ -561,22 +561,22 @@ class ListStyleType { factory ListStyleType.fromWidget(Widget widget) => ListStyleType("", widget: widget, type: "widget"); - static const LOWER_ALPHA = ListStyleType("LOWER_ALPHA"); - static const UPPER_ALPHA = ListStyleType("UPPER_ALPHA"); - static const LOWER_LATIN = ListStyleType("LOWER_LATIN"); - static const UPPER_LATIN = ListStyleType("UPPER_LATIN"); - static const CIRCLE = ListStyleType("CIRCLE"); - static const DISC = ListStyleType("DISC"); - static const DECIMAL = ListStyleType("DECIMAL"); - static const LOWER_ROMAN = ListStyleType("LOWER_ROMAN"); - static const UPPER_ROMAN = ListStyleType("UPPER_ROMAN"); - static const SQUARE = ListStyleType("SQUARE"); - static const NONE = ListStyleType("NONE"); + static const lowerAlpha = ListStyleType("LOWER_ALPHA"); + static const upperAlpha = ListStyleType("UPPER_ALPHA"); + static const lowerLatin = ListStyleType("LOWER_LATIN"); + static const upperLatin = ListStyleType("UPPER_LATIN"); + static const circle = ListStyleType("CIRCLE"); + static const disc = ListStyleType("DISC"); + static const decimal = ListStyleType("DECIMAL"); + static const lowerRoman = ListStyleType("LOWER_ROMAN"); + static const upperRoman = ListStyleType("UPPER_ROMAN"); + static const square = ListStyleType("SQUARE"); + static const none = ListStyleType("NONE"); } enum ListStylePosition { - OUTSIDE, - INSIDE, + outside, + inside, } enum TextTransform { @@ -587,12 +587,12 @@ enum TextTransform { } enum VerticalAlign { - BASELINE, - SUB, - SUPER, + baseline, + sub, + sup, } enum WhiteSpace { - NORMAL, - PRE, + normal, + pre, } From b7b8c2da57ed183c5bb97c3d6f1a3a17924ab133 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 16:02:10 -0600 Subject: [PATCH 10/82] Fix analysis hints in test/ --- pubspec.yaml | 1 + test/flutter_html_test.dart | 4 +-- test/golden_test.dart | 2 +- test/html_parser_test.dart | 34 ++++++++++------------ test/image_render_source_matcher_test.dart | 4 +-- test/style/fontsize_test.dart | 22 +++++++------- test/utils_test.dart | 10 +++---- 7 files changed, 37 insertions(+), 40 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index d1ae1407ab..9799b727ec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,5 +27,6 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.1 + meta: ^1.8.0 flutter: diff --git a/test/flutter_html_test.dart b/test/flutter_html_test.dart index 9a5a752c70..1164d010c9 100644 --- a/test/flutter_html_test.dart +++ b/test/flutter_html_test.dart @@ -61,7 +61,7 @@ void main() { MaterialApp( home: Html( data: "

Text

", - tagsList: ['div'], //Anything but `p` + tagsList: const ['div'], //Anything but `p` ), ), ); @@ -73,7 +73,7 @@ void main() { MaterialApp( home: Html( data: "

Text

", - tagsList: ['html', 'body', 'p'], + tagsList: const ['html', 'body', 'p'], ), ), ); diff --git a/test/golden_test.dart b/test/golden_test.dart index 131ee5a948..4f50fe606b 100644 --- a/test/golden_test.dart +++ b/test/golden_test.dart @@ -7,7 +7,7 @@ import 'test_data.dart'; class TestApp extends StatelessWidget { final Widget body; - TestApp(this.body); + const TestApp(this.body, {super.key}); @override Widget build(BuildContext context) { diff --git a/test/html_parser_test.dart b/test/html_parser_test.dart index 3937c4a296..69a52a2849 100644 --- a/test/html_parser_test.dart +++ b/test/html_parser_test.dart @@ -22,7 +22,7 @@ void main() { testNewParser(context); // The builder function must return a widget. - return Placeholder(); + return const Placeholder(); }, ), ); @@ -32,7 +32,7 @@ void main() { void testNewParser(BuildContext context) { HtmlParser.parseHTML("Hello, World!"); - StyledElement tree = HtmlParser.lexDomTree( + HtmlParser.lexDomTree( HtmlParser.parseHTML( "Hello! Hello, World!Hello, New World!"), [], @@ -49,16 +49,15 @@ void testNewParser(BuildContext context) { onImageError: null, shrinkWrap: false, selectable: true, - style: {}, + style: const {}, customRenders: generateDefaultRenders(), tagsList: Html.tags, selectionControls: null, scrollPhysics: null, ), ); - print(tree.toString()); - tree = HtmlParser.lexDomTree( + HtmlParser.lexDomTree( HtmlParser.parseHTML( "Hello, World! This is a link"), [], @@ -75,23 +74,22 @@ void testNewParser(BuildContext context) { onImageError: null, shrinkWrap: false, selectable: true, - style: {}, + style: const {}, customRenders: generateDefaultRenders(), tagsList: Html.tags, selectionControls: null, scrollPhysics: null, ), ); - print(tree.toString()); - tree = HtmlParser.lexDomTree( - HtmlParser.parseHTML(""), + HtmlParser.lexDomTree( + HtmlParser.parseHTML(""), [], Html.tags, context, HtmlParser( key: null, - htmlData: HtmlParser.parseHTML(""), + htmlData: HtmlParser.parseHTML(""), onLinkTap: null, onAnchorTap: null, onImageTap: null, @@ -99,25 +97,24 @@ void testNewParser(BuildContext context) { onImageError: null, shrinkWrap: false, selectable: true, - style: {}, + style: const {}, customRenders: generateDefaultRenders(), tagsList: Html.tags, selectionControls: null, scrollPhysics: null, ), ); - print(tree.toString()); - tree = HtmlParser.lexDomTree( + HtmlParser.lexDomTree( HtmlParser.parseHTML( - "
Link
Hello, World! Bold and Italic
"), + "
Link
Hello, World! Bold and Italic
"), [], Html.tags, context, HtmlParser( key: null, htmlData: HtmlParser.parseHTML( - "
Link
Hello, World! Bold and Italic
"), + "
Link
Hello, World! Bold and Italic
"), onLinkTap: null, onAnchorTap: null, onImageTap: null, @@ -125,14 +122,13 @@ void testNewParser(BuildContext context) { onImageError: null, shrinkWrap: false, selectable: true, - style: {}, + style: const {}, customRenders: generateDefaultRenders(), tagsList: Html.tags, selectionControls: null, scrollPhysics: null, ), ); - print(tree.toString()); /*ReplacedElement videoContentElement = parseReplacedElement( HtmlParser.parseHTML(""" @@ -174,7 +170,7 @@ void testNewParser(BuildContext context) { }*/ Style style1 = Style( - display: Display.BLOCK, + display: Display.block, fontWeight: FontWeight.bold, ); @@ -186,7 +182,7 @@ void testNewParser(BuildContext context) { Style finalStyle = style1.merge(style2); - expect(finalStyle.display, equals(Display.BLOCK)); + expect(finalStyle.display, equals(Display.block)); expect(finalStyle.before, equals("* ")); expect(finalStyle.direction, equals(TextDirection.rtl)); expect(finalStyle.fontStyle, equals(FontStyle.italic)); diff --git a/test/image_render_source_matcher_test.dart b/test/image_render_source_matcher_test.dart index a259d161d2..6eb674865a 100644 --- a/test/image_render_source_matcher_test.dart +++ b/test/image_render_source_matcher_test.dart @@ -179,7 +179,7 @@ void main() { String _fakeElement(String? src) { return """ - + """; } @@ -198,7 +198,7 @@ void testImgSrcMatcher( customRenders: { matcher: CustomRender.widget( widget: (RenderContext context, _) { - return Text("Success"); + return const Text("Success"); }, ), }, diff --git a/test/style/fontsize_test.dart b/test/style/fontsize_test.dart index e985d73706..84433eb890 100644 --- a/test/style/fontsize_test.dart +++ b/test/style/fontsize_test.dart @@ -5,7 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { test('Check basic FontSize inheritance', () { final FontSize parent = FontSize(16); - final FontSize? child = null; + const FontSize? child = null; final result = FontSize.inherit(parent, child); @@ -13,8 +13,8 @@ void main() { }); test('Check double null FontSize inheritance', () { - final FontSize? parent = null; - final FontSize? child = null; + const FontSize? parent = null; + const FontSize? child = null; final result = FontSize.inherit(parent, child); @@ -22,8 +22,8 @@ void main() { }); test('Check basic em inheritance', () { - final FontSize? parent = FontSize(16); - final FontSize? child = FontSize(1, Unit.em); + final FontSize parent = FontSize(16); + final FontSize child = FontSize(1, Unit.em); final result = FontSize.inherit(parent, child); @@ -31,8 +31,8 @@ void main() { }); test('Check factor em inheritance', () { - final FontSize? parent = FontSize(16); - final FontSize? child = FontSize(0.5, Unit.em); + final FontSize parent = FontSize(16); + final FontSize child = FontSize(0.5, Unit.em); final result = FontSize.inherit(parent, child); @@ -40,8 +40,8 @@ void main() { }); test('Check basic % inheritance', () { - final FontSize? parent = FontSize(16); - final FontSize? child = FontSize(100, Unit.percent); + final FontSize parent = FontSize(16); + final FontSize child = FontSize(100, Unit.percent); final result = FontSize.inherit(parent, child); @@ -49,8 +49,8 @@ void main() { }); test('Check scaled % inheritance', () { - final FontSize? parent = FontSize(16); - final FontSize? child = FontSize(50, Unit.percent); + final FontSize parent = FontSize(16); + final FontSize child = FontSize(50, Unit.percent); final result = FontSize.inherit(parent, child); diff --git a/test/utils_test.dart b/test/utils_test.dart index c4cec61f93..571c23af8c 100644 --- a/test/utils_test.dart +++ b/test/utils_test.dart @@ -13,8 +13,8 @@ void main() { test('CustomBorderSide does not allow negative width', () { expect(() => CustomBorderSide(width: -5), throwsAssertionError); - expect(CustomBorderSide(width: 0), TypeMatcher()); - expect(CustomBorderSide(width: 5), TypeMatcher()); + expect(CustomBorderSide(width: 0), const TypeMatcher()); + expect(CustomBorderSide(width: 5), const TypeMatcher()); }); const originalString = 'Hello'; @@ -25,18 +25,18 @@ void main() { expect(originalString.transformed(null), equals(originalString)); }); - test('TextTransformUtil uppercases correctly', () { + test('TextTransformUtil uppercase-s correctly', () { expect(originalString.transformed(TextTransform.uppercase), equals(uppercaseString)); }); - test('TextTransformUtil lowercases correctly', () { + test('TextTransformUtil lowercase-s correctly', () { expect(originalString.transformed(TextTransform.lowercase), equals(lowercaseString)); }); const originalLongString = 'Hello, world! pub.dev'; const capitalizedLongString = 'Hello, World! Pub.Dev'; - test('TextTransformUtil capitalizs correctly', () { + test('TextTransformUtil capitalizes correctly', () { expect(originalLongString.transformed(TextTransform.capitalize), equals(capitalizedLongString)); }); } \ No newline at end of file From 0976e7ece5adc68c51f9fee976cb911bc2d3cac8 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 16:33:36 -0600 Subject: [PATCH 11/82] Fix analysis hints in example/ --- example/lib/main.dart | 64 ++++++++++--------- .../lib/flutter_html_math.dart | 2 + 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index eea3fc5cc1..c52e9551c4 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,31 +1,32 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_html_all/flutter_html_all.dart'; -import 'package:flutter_math_fork/flutter_math.dart'; -void main() => runApp(new MyApp()); +void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { + const MyApp({super.key}); + // This widget is the root of your application. @override Widget build(BuildContext context) { - return new MaterialApp( + return MaterialApp( title: 'Flutter Demo', - theme: new ThemeData( + theme: ThemeData( primarySwatch: Colors.deepPurple, ), - home: new MyHomePage(title: 'flutter_html Example'), + home: const MyHomePage(title: 'flutter_html Example'), ); } } class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, required this.title}) : super(key: key); + const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override - _MyHomePageState createState() => new _MyHomePageState(); + MyHomePageState createState() => MyHomePageState(); } const htmlData = r""" @@ -66,9 +67,9 @@ const htmlData = r"""
margin: 15px auto
margin-left: auto

With an image - non-block (should not center):

- +

block image (should center):

- +

Table support (with custom styling!):

Famous quote... @@ -253,16 +254,16 @@ const htmlData = r""" final staticAnchorKey = GlobalKey(); -class _MyHomePageState extends State { +class MyHomePageState extends State { @override Widget build(BuildContext context) { - return new Scaffold( + return Scaffold( appBar: AppBar( - title: Text('flutter_html Example'), + title: const Text('flutter_html Example'), centerTitle: true, ), floatingActionButton: FloatingActionButton( - child: Icon(Icons.arrow_downward), + child: const Icon(Icons.arrow_downward), onPressed: () { final anchorContext = AnchorKey.forId(staticAnchorKey, "bottom")?.currentContext; if (anchorContext != null) { @@ -276,17 +277,17 @@ class _MyHomePageState extends State { data: htmlData, style: { "table": Style( - backgroundColor: Color.fromARGB(0x50, 0xee, 0xee, 0xee), + backgroundColor: const Color.fromARGB(0x50, 0xee, 0xee, 0xee), ), "tr": Style( - border: Border(bottom: BorderSide(color: Colors.grey)), + border: const Border(bottom: BorderSide(color: Colors.grey)), ), "th": Style( - padding: EdgeInsets.all(6), + padding: const EdgeInsets.all(6), backgroundColor: Colors.grey, ), "td": Style( - padding: EdgeInsets.all(6), + padding: const EdgeInsets.all(6), alignment: Alignment.topLeft, ), 'h5': Style(maxLines: 2, textOverflow: TextOverflow.ellipsis), @@ -301,7 +302,7 @@ class _MyHomePageState extends State { return Text(e.message); }, )), - tagMatcher("bird"): CustomRender.inlineSpan(inlineSpan: (context, buildChildren) => TextSpan(text: "🐦")), + tagMatcher("bird"): CustomRender.inlineSpan(inlineSpan: (context, buildChildren) => const TextSpan(text: "🐦")), tagMatcher("flutter"): CustomRender.widget(widget: (context, buildChildren) => FlutterLogo( style: (context.tree.element!.attributes['horizontal'] != null) ? FlutterLogoStyle.horizontal @@ -316,7 +317,7 @@ class _MyHomePageState extends State { audioMatcher(): audioRender(), iframeMatcher(): iframeRender(), mathMatcher(): mathRender(onMathError: (error, exception, exceptionWithType) { - print(exception); + debugPrint(exception); return Text(exception); }), svgTagMatcher(): svgTagRender(), @@ -325,36 +326,37 @@ class _MyHomePageState extends State { svgNetworkSourceMatcher(): svgNetworkImageRender(), networkSourceMatcher(domains: ["flutter.dev"]): CustomRender.widget( widget: (context, buildChildren) { - return FlutterLogo(size: 36); + return const FlutterLogo(size: 36); }), networkSourceMatcher(domains: ["mydomain.com"]): networkImageRender( headers: {"Custom-Header": "some-value"}, altWidget: (alt) => Text(alt ?? ""), - loadingWidget: () => Text("Loading..."), + loadingWidget: () => const Text("Loading..."), ), // On relative paths starting with /wiki, prefix with a base url (context) => context.tree.element?.attributes["src"] != null && context.tree.element!.attributes["src"]!.startsWith("/wiki"): - networkImageRender(mapUrl: (url) => "https://upload.wikimedia.org" + url!), + networkImageRender(mapUrl: (url) => "https://upload.wikimedia.org${url!}"), // Custom placeholder image for broken links - networkSourceMatcher(): networkImageRender(altWidget: (_) => FlutterLogo()), + networkSourceMatcher(): networkImageRender(altWidget: (_) => const FlutterLogo()), videoMatcher(): videoRender(), }, onLinkTap: (url, _, __, ___) { - print("Opening $url..."); + debugPrint("Opening $url..."); }, onImageTap: (src, _, __, ___) { - print(src); + debugPrint(src); }, onImageError: (exception, stackTrace) { - print(exception); + debugPrint(exception.toString()); }, onCssParseError: (css, messages) { - print("css that errored: $css"); - print("error messages:"); - messages.forEach((element) { - print(element); - }); + debugPrint("css that errored: $css"); + debugPrint("error messages:"); + for (var element in messages) { + debugPrint(element.toString()); + } + return ''; }, ), ), diff --git a/packages/flutter_html_math/lib/flutter_html_math.dart b/packages/flutter_html_math/lib/flutter_html_math.dart index 2af8ce170e..9e7a2908df 100644 --- a/packages/flutter_html_math/lib/flutter_html_math.dart +++ b/packages/flutter_html_math/lib/flutter_html_math.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_math_fork/flutter_math.dart'; +export 'package:flutter_math_fork/flutter_math.dart'; + /// The CustomRender function for the tag. CustomRender mathRender({OnMathError? onMathError}) => CustomRender.widget(widget: (context, buildChildren) { From b74054c595196ed0013b83095d0e92c47dfb02fe Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 16:35:59 -0600 Subject: [PATCH 12/82] Fix analysis hints in packages/flutter_html_audio --- packages/flutter_html_audio/lib/flutter_html_audio.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/flutter_html_audio/lib/flutter_html_audio.dart b/packages/flutter_html_audio/lib/flutter_html_audio.dart index c542f12242..04f7000286 100644 --- a/packages/flutter_html_audio/lib/flutter_html_audio.dart +++ b/packages/flutter_html_audio/lib/flutter_html_audio.dart @@ -28,10 +28,10 @@ class AudioWidget extends StatefulWidget { final RenderContext context; final AudioControllerCallback? callback; - AudioWidget({ + const AudioWidget({Key? key, required this.context, this.callback, - }); + }) : super(key: key); @override State createState() => _AudioWidgetState(); @@ -78,16 +78,16 @@ class _AudioWidgetState extends State { @override Widget build(BuildContext bContext) { if (sources.isEmpty || sources.first == null) { - return Container(height: 0, width: 0); + return const SizedBox(height: 0, width: 0); } return CssBoxWidget( key: widget.context.key, style: widget.context.style, + childIsReplaced: true, child: ChewieAudio( controller: chewieAudioController!, ), - childIsReplaced: true, ); } } From a346914ab2d3882c27f90a50643823fd3caa013f Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 16:38:39 -0600 Subject: [PATCH 13/82] Fix analysis hints in flutter_html_iframe --- packages/flutter_html_iframe/lib/iframe_mobile.dart | 2 +- packages/flutter_html_iframe/lib/iframe_unsupported.dart | 4 +--- packages/flutter_html_iframe/lib/iframe_web.dart | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/flutter_html_iframe/lib/iframe_mobile.dart b/packages/flutter_html_iframe/lib/iframe_mobile.dart index b35f7c7e3b..f6fb7eb683 100644 --- a/packages/flutter_html_iframe/lib/iframe_mobile.dart +++ b/packages/flutter_html_iframe/lib/iframe_mobile.dart @@ -12,7 +12,7 @@ CustomRender iframeRender({NavigationDelegate? navigationDelegate}) => double.tryParse(context.tree.element?.attributes['width'] ?? ""); final givenHeight = double.tryParse(context.tree.element?.attributes['height'] ?? ""); - return Container( + return SizedBox( width: givenWidth ?? (givenHeight ?? 150) * 2, height: givenHeight ?? (givenWidth ?? 300) / 2, child: CssBoxWidget( diff --git a/packages/flutter_html_iframe/lib/iframe_unsupported.dart b/packages/flutter_html_iframe/lib/iframe_unsupported.dart index 588552d36f..6324d9575a 100644 --- a/packages/flutter_html_iframe/lib/iframe_unsupported.dart +++ b/packages/flutter_html_iframe/lib/iframe_unsupported.dart @@ -4,7 +4,5 @@ import 'package:webview_flutter/webview_flutter.dart'; CustomRender iframeRender({NavigationDelegate? navigationDelegate}) => CustomRender.widget(widget: (context, buildChildren) { - return Container( - child: Text("Iframes are currently not supported in this environment"), - ); + return const Text("Iframes are currently not supported in this environment"); }); diff --git a/packages/flutter_html_iframe/lib/iframe_web.dart b/packages/flutter_html_iframe/lib/iframe_web.dart index aaf81c23ff..78d7e7d092 100644 --- a/packages/flutter_html_iframe/lib/iframe_web.dart +++ b/packages/flutter_html_iframe/lib/iframe_web.dart @@ -23,7 +23,7 @@ CustomRender iframeRender({NavigationDelegate? navigationDelegate}) => final String createdViewId = getRandString(10); ui.platformViewRegistry .registerViewFactory(createdViewId, (int viewId) => iframe); - return Container( + return SizedBox( width: double.tryParse(context.tree.element?.attributes['width'] ?? "") ?? (double.tryParse( From 8bdb767b3a08d0610c9da64cf1d281efd0f1d528 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 16:41:28 -0600 Subject: [PATCH 14/82] Fix analysis hints in flutter_html_math --- .../lib/flutter_html_math.dart | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/flutter_html_math/lib/flutter_html_math.dart b/packages/flutter_html_math/lib/flutter_html_math.dart index 9e7a2908df..8c7e764135 100644 --- a/packages/flutter_html_math/lib/flutter_html_math.dart +++ b/packages/flutter_html_math/lib/flutter_html_math.dart @@ -13,7 +13,7 @@ CustomRender mathRender({OnMathError? onMathError}) => String texStr = context.tree.element == null ? '' : _parseMathRecursive(context.tree.element!, r''); - return Container( + return SizedBox( width: context.parser.shrinkWrap ? null : MediaQuery.of(context.buildContext).size.width, @@ -40,9 +40,9 @@ String _parseMathRecursive(dom.Node node, String parsed) { if (node is dom.Element) { List nodeList = node.nodes.whereType().toList(); if (node.localName == "math" || node.localName == "mrow") { - nodeList.forEach((element) { + for (var element in nodeList) { parsed = _parseMathRecursive(element, parsed); - }); + } } // note: munder, mover, and munderover do not support placing braces and other // markings above/below elements, instead they are treated as super/subscripts for now. @@ -52,37 +52,35 @@ String _parseMathRecursive(dom.Node node, String parsed) { node.localName == "mover") && nodeList.length == 2) { parsed = _parseMathRecursive(nodeList[0], parsed); - parsed = _parseMathRecursive( + parsed = "${_parseMathRecursive( nodeList[1], - parsed + - "${node.localName == "msup" || node.localName == "mover" ? "^" : "_"}{") + - "}"; + "$parsed${node.localName == "msup" || node.localName == "mover" ? "^" : "_"}{")}}"; } if ((node.localName == "msubsup" || node.localName == "munderover") && nodeList.length == 3) { parsed = _parseMathRecursive(nodeList[0], parsed); - parsed = _parseMathRecursive(nodeList[1], parsed + "_{") + "}"; - parsed = _parseMathRecursive(nodeList[2], parsed + "^{") + "}"; + parsed = "${_parseMathRecursive(nodeList[1], "${parsed}_{")}}"; + parsed = "${_parseMathRecursive(nodeList[2], "$parsed^{")}}"; } if (node.localName == "mfrac" && nodeList.length == 2) { - parsed = _parseMathRecursive(nodeList[0], parsed + r"\frac{") + "}"; - parsed = _parseMathRecursive(nodeList[1], parsed + "{") + "}"; + parsed = "${_parseMathRecursive(nodeList[0], parsed + r"\frac{")}}"; + parsed = "${_parseMathRecursive(nodeList[1], "$parsed{")}}"; } // note: doesn't support answer & intermediate steps if (node.localName == "mlongdiv" && nodeList.length == 4) { parsed = _parseMathRecursive(nodeList[0], parsed); - parsed = _parseMathRecursive(nodeList[2], parsed + r"\overline{)") + "}"; + parsed = "${_parseMathRecursive(nodeList[2], parsed + r"\overline{)")}}"; } if (node.localName == "msqrt") { parsed = parsed + r"\sqrt{"; - nodeList.forEach((element) { + for (var element in nodeList) { parsed = _parseMathRecursive(element, parsed); - }); - parsed = parsed + "}"; + } + parsed = "$parsed}"; } if (node.localName == "mroot" && nodeList.length == 2) { - parsed = _parseMathRecursive(nodeList[1], parsed + r"\sqrt[") + "]"; - parsed = _parseMathRecursive(nodeList[0], parsed + "{") + "}"; + parsed = "${_parseMathRecursive(nodeList[1], parsed + r"\sqrt[")}]"; + parsed = "${_parseMathRecursive(nodeList[0], "$parsed{")}}"; } if (node.localName == "mi" || node.localName == "mn" || From f7ad078722c962b139ae2f80f91c3e4a76229e17 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 16:50:53 -0600 Subject: [PATCH 15/82] Fix analysis hints in flutter_html_svg --- packages/flutter_html_svg/lib/flutter_html_svg.dart | 10 +++++----- packages/flutter_html_svg/pubspec.yaml | 1 + .../test/svg_image_matcher_source_matcher_test.dart | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/flutter_html_svg/lib/flutter_html_svg.dart b/packages/flutter_html_svg/lib/flutter_html_svg.dart index dd5b1ff53d..a34bcceb84 100644 --- a/packages/flutter_html_svg/lib/flutter_html_svg.dart +++ b/packages/flutter_html_svg/lib/flutter_html_svg.dart @@ -42,7 +42,7 @@ CustomRender svgDataImageRender() => final dataUri = _dataUriFormat.firstMatch( _src(context.tree.element?.attributes.cast() ?? {})!); final data = dataUri?.namedGroup('data'); - if (data == null) return Container(height: 0, width: 0); + if (data == null) return const SizedBox(height: 0, width: 0); return Builder( key: context.key, builder: (buildContext) { @@ -74,7 +74,7 @@ CustomRender svgDataImageRender() => CustomRender svgNetworkImageRender() => CustomRender.widget(widget: (context, buildChildren) { if (context.tree.element?.attributes["src"] == null) { - return Container(height: 0, width: 0); + return const SizedBox(height: 0, width: 0); } return Builder( key: context.key, @@ -104,7 +104,7 @@ CustomRender svgAssetImageRender() => CustomRender.widget(widget: (context, buildChildren) { if (_src(context.tree.element?.attributes.cast() ?? {}) == null) { - return Container(height: 0, width: 0); + return const SizedBox(height: 0, width: 0); } final assetPath = _src(context.tree.element!.attributes.cast())! .replaceFirst('asset:', ''); @@ -148,7 +148,7 @@ CustomRenderMatcher svgDataUriMatcher( /// A CustomRenderMatcher for an tag with an svg tag over the network CustomRenderMatcher svgNetworkSourceMatcher({ - List schemas: const ["https", "http"], + List schemas = const ["https", "http"], List? domains, String? extension = "svg", }) => @@ -178,7 +178,7 @@ CustomRenderMatcher svgAssetUriMatcher() => (context) => .endsWith(".svg"); final _dataUriFormat = RegExp( - "^(?data):(?image\/[\\w\+\-\.]+)(?;base64)?\,(?.*)"); + "^(?data):(?image\\/[\\w\\+\\-\\.]+)(?;base64)?\\,(?.*)"); String? _src(Map attributes) { return attributes["src"]; diff --git a/packages/flutter_html_svg/pubspec.yaml b/packages/flutter_html_svg/pubspec.yaml index b1b4089520..85e9464ef5 100644 --- a/packages/flutter_html_svg/pubspec.yaml +++ b/packages/flutter_html_svg/pubspec.yaml @@ -21,5 +21,6 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.1 + meta: ^1.8.0 flutter: diff --git a/packages/flutter_html_svg/test/svg_image_matcher_source_matcher_test.dart b/packages/flutter_html_svg/test/svg_image_matcher_source_matcher_test.dart index eb31307d7a..21debbb8a4 100644 --- a/packages/flutter_html_svg/test/svg_image_matcher_source_matcher_test.dart +++ b/packages/flutter_html_svg/test/svg_image_matcher_source_matcher_test.dart @@ -64,7 +64,7 @@ void main() { String _fakeElement(String? src) { return """ - + """; } @@ -83,7 +83,7 @@ void testImgSrcMatcher( customRenders: { matcher: CustomRender.widget( widget: (RenderContext context, _) { - return Text("Success"); + return const Text("Success"); }, ), }, @@ -98,14 +98,14 @@ void testImgSrcMatcher( class TestApp extends StatelessWidget { final Widget body; - TestApp(this.body); + const TestApp(this.body, {Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: body, - appBar: AppBar(title: Text('flutter_html')), + appBar: AppBar(title: const Text('flutter_html')), ), ); } From 42d3a72b0b82e4a6a8c206d9a203c84c07a5c004 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 16:53:02 -0600 Subject: [PATCH 16/82] Fix analysis hints in flutter_html_table --- .../lib/flutter_html_table.dart | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/flutter_html_table/lib/flutter_html_table.dart b/packages/flutter_html_table/lib/flutter_html_table.dart index 8a207abbf3..8c5d487d45 100644 --- a/packages/flutter_html_table/lib/flutter_html_table.dart +++ b/packages/flutter_html_table/lib/flutter_html_table.dart @@ -38,20 +38,20 @@ Widget _layoutCells(RenderContext context, BoxConstraints constraints) { if (colWidth != null && colWidth.endsWith("%")) { if (!constraints.hasBoundedWidth) { // In a horizontally unbounded container; always wrap content instead of applying flex - return IntrinsicContentTrackSize(); + return const IntrinsicContentTrackSize(); } final percentageSize = double.tryParse(colWidth.substring(0, colWidth.length - 1)); return percentageSize != null && !percentageSize.isNaN ? FlexibleTrackSize(percentageSize * 0.01) - : IntrinsicContentTrackSize(); + : const IntrinsicContentTrackSize(); } else if (colWidth != null) { final fixedPxSize = double.tryParse(colWidth); return fixedPxSize != null ? FixedTrackSize(fixedPxSize) - : IntrinsicContentTrackSize(); + : const IntrinsicContentTrackSize(); } else { - return IntrinsicContentTrackSize(); + return const IntrinsicContentTrackSize(); } }); }) @@ -66,7 +66,7 @@ Widget _layoutCells(RenderContext context, BoxConstraints constraints) { // All table rows have a height intrinsic to their (spanned) contents final rowSizes = - List.generate(rows.length, (_) => IntrinsicContentTrackSize()); + List.generate(rows.length, (_) => const IntrinsicContentTrackSize()); // Calculate column bounds int columnMax = 0; @@ -103,6 +103,10 @@ Widget _layoutCells(RenderContext context, BoxConstraints constraints) { columnColspanOffset[columni].clamp(1, columnMax - columni - 1); } cells.add(GridPlacement( + columnStart: columni, + columnSpan: min(child.colspan, columnMax - columni), + rowStart: rowi, + rowSpan: min(child.rowspan, rows.length - rowi), child: CssBoxWidget( style: child.style .merge(row.style), //TODO padding/decoration(color/border) @@ -118,10 +122,6 @@ Widget _layoutCells(RenderContext context, BoxConstraints constraints) { ), ), ), - columnStart: columni, - columnSpan: min(child.colspan, columnMax - columni), - rowStart: rowi, - rowSpan: min(child.rowspan, rows.length - rowi), )); columnRowOffset[columni] = child.rowspan - 1; columnColspanOffset[columni] = child.colspan; @@ -138,11 +138,11 @@ Widget _layoutCells(RenderContext context, BoxConstraints constraints) { // Create column tracks (insofar there were no colgroups that already defined them) List finalColumnSizes = columnSizes.take(columnMax).toList(); finalColumnSizes += List.generate(max(0, columnMax - finalColumnSizes.length), - (_) => IntrinsicContentTrackSize()); + (_) => const IntrinsicContentTrackSize()); if (finalColumnSizes.isEmpty || rowSizes.isEmpty) { // No actual cells to show - return SizedBox(); + return const SizedBox(); } return LayoutGrid( From 87e15759edc7ed7c2c9fcf746b2a2dedd739c28c Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 16:53:59 -0600 Subject: [PATCH 17/82] Fix analysis hints in flutter_html_video --- packages/flutter_html_video/lib/flutter_html_video.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/flutter_html_video/lib/flutter_html_video.dart b/packages/flutter_html_video/lib/flutter_html_video.dart index 2dc6faa62d..86790d7a1a 100644 --- a/packages/flutter_html_video/lib/flutter_html_video.dart +++ b/packages/flutter_html_video/lib/flutter_html_video.dart @@ -25,10 +25,10 @@ class VideoWidget extends StatefulWidget { final RenderContext context; final VideoControllerCallback? callback; - VideoWidget({ + const VideoWidget({Key? key, required this.context, this.callback, - }); + }) : super(key: key); @override State createState() => _VideoWidgetState(); @@ -83,7 +83,7 @@ class _VideoWidgetState extends State { @override Widget build(BuildContext bContext) { if (_chewieController == null) { - return Container(height: 0, width: 0); + return const SizedBox(height: 0, width: 0); } final child = Container( key: widget.context.key, From abf5274219af92e5924d1760e4b19a21c40ab5ef Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 26 Sep 2022 16:59:07 -0600 Subject: [PATCH 18/82] Run flutter formatter --- example/lib/main.dart | 83 +++++++++++-------- lib/custom_render.dart | 8 +- lib/flutter_html.dart | 3 +- lib/html_parser.dart | 16 ++-- lib/src/css_box_widget.dart | 8 +- lib/src/css_parser.dart | 32 +++---- lib/src/html_elements.dart | 4 +- lib/src/replaced_element.dart | 1 - lib/src/style/length.dart | 6 +- .../lib/flutter_html_audio.dart | 3 +- .../lib/iframe_unsupported.dart | 3 +- .../lib/flutter_html_math.dart | 5 +- .../lib/flutter_html_table.dart | 2 +- .../lib/flutter_html_video.dart | 3 +- test/flutter_html_test.dart | 10 ++- test/golden_test.dart | 5 +- test/html_parser_test.dart | 9 +- test/image_render_source_matcher_test.dart | 15 ++-- test/style/dimension_test.dart | 2 +- test/style/fontsize_test.dart | 2 +- test/test_data.dart | 36 +++++--- test/utils_test.dart | 14 ++-- 22 files changed, 155 insertions(+), 115 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index c52e9551c4..8ff39ed120 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -265,7 +265,8 @@ class MyHomePageState extends State { floatingActionButton: FloatingActionButton( child: const Icon(Icons.arrow_downward), onPressed: () { - final anchorContext = AnchorKey.forId(staticAnchorKey, "bottom")?.currentContext; + final anchorContext = + AnchorKey.forId(staticAnchorKey, "bottom")?.currentContext; if (anchorContext != null) { Scrollable.ensureVisible(anchorContext); } @@ -294,29 +295,39 @@ class MyHomePageState extends State { }, tagsList: Html.tags..addAll(['tex', 'bird', 'flutter']), customRenders: { - tagMatcher("tex"): CustomRender.widget(widget: (context, buildChildren) => Math.tex( - context.tree.element?.innerHtml ?? '', - mathStyle: MathStyle.display, - textStyle: context.style.generateTextStyle(), - onErrorFallback: (FlutterMathException e) { - return Text(e.message); - }, - )), - tagMatcher("bird"): CustomRender.inlineSpan(inlineSpan: (context, buildChildren) => const TextSpan(text: "🐦")), - tagMatcher("flutter"): CustomRender.widget(widget: (context, buildChildren) => FlutterLogo( - style: (context.tree.element!.attributes['horizontal'] != null) - ? FlutterLogoStyle.horizontal - : FlutterLogoStyle.markOnly, - textColor: context.style.color!, - size: context.style.fontSize!.value * 5, - )), - tagMatcher("table"): CustomRender.widget(widget: (context, buildChildren) => SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: tableRender.call().widget!.call(context, buildChildren), - )), + tagMatcher("tex"): CustomRender.widget( + widget: (context, buildChildren) => Math.tex( + context.tree.element?.innerHtml ?? '', + mathStyle: MathStyle.display, + textStyle: context.style.generateTextStyle(), + onErrorFallback: (FlutterMathException e) { + return Text(e.message); + }, + )), + tagMatcher("bird"): CustomRender.inlineSpan( + inlineSpan: (context, buildChildren) => + const TextSpan(text: "🐦")), + tagMatcher("flutter"): CustomRender.widget( + widget: (context, buildChildren) => FlutterLogo( + style: (context.tree.element!.attributes['horizontal'] != + null) + ? FlutterLogoStyle.horizontal + : FlutterLogoStyle.markOnly, + textColor: context.style.color!, + size: context.style.fontSize!.value * 5, + )), + tagMatcher("table"): CustomRender.widget( + widget: (context, buildChildren) => SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: tableRender + .call() + .widget! + .call(context, buildChildren), + )), audioMatcher(): audioRender(), iframeMatcher(): iframeRender(), - mathMatcher(): mathRender(onMathError: (error, exception, exceptionWithType) { + mathMatcher(): + mathRender(onMathError: (error, exception, exceptionWithType) { debugPrint(exception); return Text(exception); }), @@ -324,21 +335,24 @@ class MyHomePageState extends State { svgDataUriMatcher(): svgDataImageRender(), svgAssetUriMatcher(): svgAssetImageRender(), svgNetworkSourceMatcher(): svgNetworkImageRender(), - networkSourceMatcher(domains: ["flutter.dev"]): CustomRender.widget( - widget: (context, buildChildren) { - return const FlutterLogo(size: 36); - }), + networkSourceMatcher(domains: ["flutter.dev"]): + CustomRender.widget(widget: (context, buildChildren) { + return const FlutterLogo(size: 36); + }), networkSourceMatcher(domains: ["mydomain.com"]): networkImageRender( headers: {"Custom-Header": "some-value"}, altWidget: (alt) => Text(alt ?? ""), loadingWidget: () => const Text("Loading..."), ), // On relative paths starting with /wiki, prefix with a base url - (context) => context.tree.element?.attributes["src"] != null - && context.tree.element!.attributes["src"]!.startsWith("/wiki"): - networkImageRender(mapUrl: (url) => "https://upload.wikimedia.org${url!}"), + (context) => + context.tree.element?.attributes["src"] != null && + context.tree.element!.attributes["src"]! + .startsWith("/wiki"): networkImageRender( + mapUrl: (url) => "https://upload.wikimedia.org${url!}"), // Custom placeholder image for broken links - networkSourceMatcher(): networkImageRender(altWidget: (_) => const FlutterLogo()), + networkSourceMatcher(): + networkImageRender(altWidget: (_) => const FlutterLogo()), videoMatcher(): videoRender(), }, onLinkTap: (url, _, __, ___) { @@ -364,8 +378,11 @@ class MyHomePageState extends State { } } -CustomRenderMatcher texMatcher() => (context) => context.tree.element?.localName == 'tex'; +CustomRenderMatcher texMatcher() => + (context) => context.tree.element?.localName == 'tex'; -CustomRenderMatcher birdMatcher() => (context) => context.tree.element?.localName == 'bird'; +CustomRenderMatcher birdMatcher() => + (context) => context.tree.element?.localName == 'bird'; -CustomRenderMatcher flutterMatcher() => (context) => context.tree.element?.localName == 'flutter'; +CustomRenderMatcher flutterMatcher() => + (context) => context.tree.element?.localName == 'flutter'; diff --git a/lib/custom_render.dart b/lib/custom_render.dart index 2a0bdea729..35e678beec 100644 --- a/lib/custom_render.dart +++ b/lib/custom_render.dart @@ -370,12 +370,16 @@ CustomRender networkImageRender({ if (!completer.isCompleted) { context.parser.cachedImageSizes[src] = size; completer.complete(size); - image.image.resolve(const ImageConfiguration()).removeListener(listener!); + image.image + .resolve(const ImageConfiguration()) + .removeListener(listener!); } }, onError: (object, stacktrace) { if (!completer.isCompleted) { completer.completeError(object); - image.image.resolve(const ImageConfiguration()).removeListener(listener!); + image.image + .resolve(const ImageConfiguration()) + .removeListener(listener!); } }); diff --git a/lib/flutter_html.dart b/lib/flutter_html.dart index b702a2c24c..1b5d7fe55e 100644 --- a/lib/flutter_html.dart +++ b/lib/flutter_html.dart @@ -327,7 +327,8 @@ class SelectableHtml extends StatefulWidget { /// fallback to the default rendering. final Map customRenders; - static List get tags => List.from(HtmlElements.selectableElements); + static List get tags => + List.from(HtmlElements.selectableElements); @override State createState() => _SelectableHtmlState(); diff --git a/lib/html_parser.dart b/lib/html_parser.dart index 74f253ef44..8d73cc5d18 100644 --- a/lib/html_parser.dart +++ b/lib/html_parser.dart @@ -60,9 +60,8 @@ class HtmlParser extends StatelessWidget { this.root, this.selectionControls, this.scrollPhysics, - }) : internalOnAnchorTap = onAnchorTap ?? (key != null - ? _handleAnchorTap(key, onLinkTap) - : onLinkTap); + }) : internalOnAnchorTap = onAnchorTap ?? + (key != null ? _handleAnchorTap(key, onLinkTap) : onLinkTap); /// As the widget [build]s, the HTML data is processed into a tree of [StyledElement]s, /// which are then parsed into an [InlineSpan] tree that is then rendered to the screen by Flutter @@ -184,7 +183,8 @@ class HtmlParser extends StatelessWidget { return parseLayoutElement(node, children); } else if (HtmlElements.tableCellElements.contains(node.localName)) { return parseTableCellElement(node, children); - } else if (HtmlElements.tableDefinitionElements.contains(node.localName)) { + } else if (HtmlElements.tableDefinitionElements + .contains(node.localName)) { return parseTableDefinitionElement(node, children); } else { final StyledElement tree = parseStyledElement(node, children); @@ -285,7 +285,6 @@ class HtmlParser extends StatelessWidget { /// child that doesn't specify a different style. static StyledElement _cascadeStyles( Map style, StyledElement tree) { - for (var child in tree.children) { child.style = tree.style.copyOnlyInherited(child.style); _cascadeStyles(style, child); @@ -372,7 +371,7 @@ class HtmlParser extends StatelessWidget { shrinkWrap: newContext.parser.shrinkWrap, childIsReplaced: true, //TODO is this true? child: - customRenders[entry]!.widget!.call(newContext, buildChildren), + customRenders[entry]!.widget!.call(newContext, buildChildren), ), ); } @@ -655,9 +654,8 @@ class HtmlParser extends StatelessWidget { if (olStack.last.data <= 0) { marker = '${olStack.last.data}.'; } else { - marker = "${(olStack.last.data as int) - .toRomanNumeralString()! - .toLowerCase()}."; + marker = + "${(olStack.last.data as int).toRomanNumeralString()!.toLowerCase()}."; } break; case ListStyleType.upperRoman: diff --git a/lib/src/css_box_widget.dart b/lib/src/css_box_widget.dart index 2dd59a52c1..727095c0f4 100644 --- a/lib/src/css_box_widget.dart +++ b/lib/src/css_box_widget.dart @@ -25,7 +25,7 @@ class CssBoxWidget extends StatelessWidget { bool selectable = false, TextSelectionControls? selectionControls, ScrollPhysics? scrollPhysics, - }) : child = selectable + }) : child = selectable ? _generateSelectableWidgetChild( children, style, @@ -676,9 +676,9 @@ extension Normalize on Dimension { void normalize(double emValue) { switch (unit) { case Unit.rem: - // Because CSSBoxWidget doesn't have any information about any - // sort of tree structure, treat rem the same as em. The HtmlParser - // widget handles rem/em values before they get to CSSBoxWidget. + // Because CSSBoxWidget doesn't have any information about any + // sort of tree structure, treat rem the same as em. The HtmlParser + // widget handles rem/em values before they get to CSSBoxWidget. case Unit.em: value *= emValue; unit = Unit.px; diff --git a/lib/src/css_parser.dart b/lib/src/css_parser.dart index 92fb6a5dd3..c191a2907d 100644 --- a/lib/src/css_parser.dart +++ b/lib/src/css_parser.dart @@ -1083,8 +1083,8 @@ class ExpressionMapping { // return LengthOrPercent(double.parse(value.text), Unit.rem); // TODO there are several other available terms processed by the CSS parser } else if (value is css.LengthTerm) { - double number = double.parse( - value.text.replaceAll(RegExp(r'\s+(\d+\.\d+)\s+'), '')); + double number = + double.parse(value.text.replaceAll(RegExp(r'\s+(\d+\.\d+)\s+'), '')); Unit unit = _unitMap(value.unit); return LengthOrPercent(number, unit); } @@ -1204,31 +1204,23 @@ class ExpressionMapping { shadow.add(Shadow( color: expressionToColor(color)!, offset: Offset( - double.tryParse((offsetX) - .text - .replaceAll(nonNumberRegex, ''))!, - double.tryParse((offsetY) - .text - .replaceAll(nonNumberRegex, ''))!), + double.tryParse((offsetX).text.replaceAll(nonNumberRegex, ''))!, + double.tryParse( + (offsetY).text.replaceAll(nonNumberRegex, ''))!), blurRadius: (blurRadius is css.LiteralTerm) - ? double.tryParse((blurRadius) - .text - .replaceAll(nonNumberRegex, ''))! + ? double.tryParse( + (blurRadius).text.replaceAll(nonNumberRegex, ''))! : 0.0, )); } else { shadow.add(Shadow( offset: Offset( - double.tryParse((offsetX) - .text - .replaceAll(nonNumberRegex, ''))!, - double.tryParse((offsetY) - .text - .replaceAll(nonNumberRegex, ''))!), + double.tryParse((offsetX).text.replaceAll(nonNumberRegex, ''))!, + double.tryParse( + (offsetY).text.replaceAll(nonNumberRegex, ''))!), blurRadius: (blurRadius is css.LiteralTerm) - ? double.tryParse((blurRadius) - .text - .replaceAll(nonNumberRegex, ''))! + ? double.tryParse( + (blurRadius).text.replaceAll(nonNumberRegex, ''))! : 0.0, )); } diff --git a/lib/src/html_elements.dart b/lib/src/html_elements.dart index 6e2bccb37c..304c9ff395 100644 --- a/lib/src/html_elements.dart +++ b/lib/src/html_elements.dart @@ -3,7 +3,6 @@ export 'interactable_element.dart'; export 'replaced_element.dart'; class HtmlElements { - static const styledElements = [ "abbr", "acronym", @@ -205,5 +204,4 @@ class HtmlElements { "var", "wbr", ]; - -} \ No newline at end of file +} diff --git a/lib/src/replaced_element.dart b/lib/src/replaced_element.dart index 28d95b7213..582c35432d 100644 --- a/lib/src/replaced_element.dart +++ b/lib/src/replaced_element.dart @@ -71,7 +71,6 @@ class EmptyContentElement extends ReplacedElement { } class RubyElement extends ReplacedElement { - @override dom.Element element; diff --git a/lib/src/style/length.dart b/lib/src/style/length.dart index f9eae57293..b63e8ebb55 100644 --- a/lib/src/style/length.dart +++ b/lib/src/style/length.dart @@ -4,7 +4,8 @@ enum UnitType { length, auto, lengthPercent(children: [UnitType.length, UnitType.percent]), - lengthPercentAuto(children: [UnitType.length, UnitType.percent, UnitType.auto]); + lengthPercentAuto( + children: [UnitType.length, UnitType.percent, UnitType.auto]); final List children; @@ -45,7 +46,8 @@ abstract class Dimension { /// This dimension takes a value with a length unit such as px or em. Note that /// these can be fixed or relative (but they must not be a percent) class Length extends Dimension { - Length(double value, [Unit unit = Unit.px]) : super(value, unit, UnitType.length); + Length(double value, [Unit unit = Unit.px]) + : super(value, unit, UnitType.length); } /// This dimension takes a value with a length-percent unit such as px or em diff --git a/packages/flutter_html_audio/lib/flutter_html_audio.dart b/packages/flutter_html_audio/lib/flutter_html_audio.dart index 04f7000286..d9dbe0825f 100644 --- a/packages/flutter_html_audio/lib/flutter_html_audio.dart +++ b/packages/flutter_html_audio/lib/flutter_html_audio.dart @@ -28,7 +28,8 @@ class AudioWidget extends StatefulWidget { final RenderContext context; final AudioControllerCallback? callback; - const AudioWidget({Key? key, + const AudioWidget({ + Key? key, required this.context, this.callback, }) : super(key: key); diff --git a/packages/flutter_html_iframe/lib/iframe_unsupported.dart b/packages/flutter_html_iframe/lib/iframe_unsupported.dart index 6324d9575a..fcc858c865 100644 --- a/packages/flutter_html_iframe/lib/iframe_unsupported.dart +++ b/packages/flutter_html_iframe/lib/iframe_unsupported.dart @@ -4,5 +4,6 @@ import 'package:webview_flutter/webview_flutter.dart'; CustomRender iframeRender({NavigationDelegate? navigationDelegate}) => CustomRender.widget(widget: (context, buildChildren) { - return const Text("Iframes are currently not supported in this environment"); + return const Text( + "Iframes are currently not supported in this environment"); }); diff --git a/packages/flutter_html_math/lib/flutter_html_math.dart b/packages/flutter_html_math/lib/flutter_html_math.dart index 8c7e764135..8785d410de 100644 --- a/packages/flutter_html_math/lib/flutter_html_math.dart +++ b/packages/flutter_html_math/lib/flutter_html_math.dart @@ -52,9 +52,8 @@ String _parseMathRecursive(dom.Node node, String parsed) { node.localName == "mover") && nodeList.length == 2) { parsed = _parseMathRecursive(nodeList[0], parsed); - parsed = "${_parseMathRecursive( - nodeList[1], - "$parsed${node.localName == "msup" || node.localName == "mover" ? "^" : "_"}{")}}"; + parsed = + "${_parseMathRecursive(nodeList[1], "$parsed${node.localName == "msup" || node.localName == "mover" ? "^" : "_"}{")}}"; } if ((node.localName == "msubsup" || node.localName == "munderover") && nodeList.length == 3) { diff --git a/packages/flutter_html_table/lib/flutter_html_table.dart b/packages/flutter_html_table/lib/flutter_html_table.dart index 8c5d487d45..b6fe35c0f4 100644 --- a/packages/flutter_html_table/lib/flutter_html_table.dart +++ b/packages/flutter_html_table/lib/flutter_html_table.dart @@ -13,7 +13,7 @@ CustomRender tableRender() => key: context.key, style: context.style, child: LayoutBuilder( - builder: (_, constraints) => _layoutCells(context, constraints), + builder: (_, constraints) => _layoutCells(context, constraints), ), ); }); diff --git a/packages/flutter_html_video/lib/flutter_html_video.dart b/packages/flutter_html_video/lib/flutter_html_video.dart index 86790d7a1a..b1ea0ca2de 100644 --- a/packages/flutter_html_video/lib/flutter_html_video.dart +++ b/packages/flutter_html_video/lib/flutter_html_video.dart @@ -25,7 +25,8 @@ class VideoWidget extends StatefulWidget { final RenderContext context; final VideoControllerCallback? callback; - const VideoWidget({Key? key, + const VideoWidget({ + Key? key, required this.context, this.callback, }) : super(key: key); diff --git a/test/flutter_html_test.dart b/test/flutter_html_test.dart index 1164d010c9..30acdbe449 100644 --- a/test/flutter_html_test.dart +++ b/test/flutter_html_test.dart @@ -33,7 +33,7 @@ void main() { testWidgets( "Check that widget displays given text", - (tester) async { + (tester) async { await tester.pumpWidget( MaterialApp( home: Html( @@ -56,7 +56,9 @@ void main() { expect(find.text('Text', findRichText: true), findsOneWidget); }); - testWidgets('Check that a simple element is hidden when tagsList does not contain it', (tester) async { + testWidgets( + 'Check that a simple element is hidden when tagsList does not contain it', + (tester) async { await tester.pumpWidget( MaterialApp( home: Html( @@ -68,7 +70,9 @@ void main() { expect(find.text('Text', findRichText: true), findsNothing); }); - testWidgets('Check that a simple element is displayed when it is included in tagsList', (tester) async { + testWidgets( + 'Check that a simple element is displayed when it is included in tagsList', + (tester) async { await tester.pumpWidget( MaterialApp( home: Html( diff --git a/test/golden_test.dart b/test/golden_test.dart index 4f50fe606b..1f62cfb414 100644 --- a/test/golden_test.dart +++ b/test/golden_test.dart @@ -62,11 +62,12 @@ void main() { // await expectLater(find.byType(Html), matchesGoldenFile('./goldens/whitespace.png')); }); - testWidgets('whitespace between inline elements golden test', (WidgetTester tester) async { + testWidgets('whitespace between inline elements golden test', + (WidgetTester tester) async { await tester.pumpWidget( TestApp( Html( - data:"""Harry Potter""", + data: """Harry Potter""", ), ), ); diff --git a/test/html_parser_test.dart b/test/html_parser_test.dart index 69a52a2849..056de9c148 100644 --- a/test/html_parser_test.dart +++ b/test/html_parser_test.dart @@ -15,7 +15,8 @@ void main() { ), ); }); - testWidgets('Test new parser (hacky workaround to get BuildContext)', (WidgetTester tester) async { + testWidgets('Test new parser (hacky workaround to get BuildContext)', + (WidgetTester tester) async { await tester.pumpWidget( Builder( builder: (BuildContext context) { @@ -33,8 +34,7 @@ void testNewParser(BuildContext context) { HtmlParser.parseHTML("Hello, World!"); HtmlParser.lexDomTree( - HtmlParser.parseHTML( - "Hello! Hello, World!Hello, New World!"), + HtmlParser.parseHTML("Hello! Hello, World!Hello, New World!"), [], Html.tags, context, @@ -89,7 +89,8 @@ void testNewParser(BuildContext context) { context, HtmlParser( key: null, - htmlData: HtmlParser.parseHTML(""), + htmlData: HtmlParser.parseHTML( + ""), onLinkTap: null, onAnchorTap: null, onImageTap: null, diff --git a/test/image_render_source_matcher_test.dart b/test/image_render_source_matcher_test.dart index 6eb674865a..579a9c2f75 100644 --- a/test/image_render_source_matcher_test.dart +++ b/test/image_render_source_matcher_test.dart @@ -87,25 +87,29 @@ void main() { testImgSrcMatcher( "matches schema, domain and extension", matcher, - imgSrc: 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png', + imgSrc: + 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png', shouldMatch: true, ); testImgSrcMatcher( "doesn't match if schema is different", matcher, - imgSrc: 'http://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png', + imgSrc: + 'http://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png', shouldMatch: false, ); testImgSrcMatcher( "doesn't match if domain is different", matcher, - imgSrc: 'https://google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png', + imgSrc: + 'https://google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png', shouldMatch: false, ); testImgSrcMatcher( "doesn't match if file extension is different", matcher, - imgSrc: 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dppng', + imgSrc: + 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dppng', shouldMatch: false, ); testImgSrcMatcher( @@ -205,6 +209,7 @@ void testImgSrcMatcher( ), ), ); - await expectLater(find.text("Success"), shouldMatch ? findsOneWidget : findsNothing); + await expectLater( + find.text("Success"), shouldMatch ? findsOneWidget : findsNothing); }); } diff --git a/test/style/dimension_test.dart b/test/style/dimension_test.dart index cf239249f2..8ddedcd32e 100644 --- a/test/style/dimension_test.dart +++ b/test/style/dimension_test.dart @@ -41,4 +41,4 @@ void main() { expect(lengthPercent.value, equals(nonZeroNumber)); expect(lengthPercent.unit, equals(Unit.percent)); }); -} \ No newline at end of file +} diff --git a/test/style/fontsize_test.dart b/test/style/fontsize_test.dart index 84433eb890..36b8e29605 100644 --- a/test/style/fontsize_test.dart +++ b/test/style/fontsize_test.dart @@ -56,4 +56,4 @@ void main() { expect(result?.value, equals(8)); }); -} \ No newline at end of file +} diff --git a/test/test_data.dart b/test/test_data.dart index c2fc16a601..f9c74ea1ce 100644 --- a/test/test_data.dart +++ b/test/test_data.dart @@ -49,12 +49,18 @@ const testData = { 'nav': '

', 'noscript': '', 'p': '

Hello, World!

', - 'p-with-inline-css-text-align-center': '

Hello, World!

', - 'p-with-inline-css-text-align-right': '

Hello, World!

', - 'p-with-inline-css-text-align-left': '

Hello, World!

', - 'p-with-inline-css-text-align-justify': '

Hello, World!

', - 'p-with-inline-css-text-align-end': '

Hello, World!

', - 'p-with-inline-css-text-align-start': '

Hello, World!

', + 'p-with-inline-css-text-align-center': + '

Hello, World!

', + 'p-with-inline-css-text-align-right': + '

Hello, World!

', + 'p-with-inline-css-text-align-left': + '

Hello, World!

', + 'p-with-inline-css-text-align-justify': + '

Hello, World!

', + 'p-with-inline-css-text-align-end': + '

Hello, World!

', + 'p-with-inline-css-text-align-start': + '

Hello, World!

', 'pre': '
Hello, World!
', 'q': 'Hello, World!', 'rp': ' ㄏㄢˋ ', @@ -65,12 +71,18 @@ const testData = { 'section': '
Hello, World!
', 'small': 'Hello, World!', 'span': 'Hello, World!', - 'span-with-inline-css-color': '

Hello, World!

', - 'span-with-inline-css-color-rgb': '

Hello, World!

', - 'span-with-inline-css-color-rgba': '

Hello, World!

', - 'span-with-inline-css-backgroundcolor': '

Hello, World!

', - 'span-with-inline-css-backgroundcolor-rgb': '

Hello, World!

', - 'span-with-inline-css-backgroundcolor-rgba': '

Hello, World!

', + 'span-with-inline-css-color': + '

Hello, World!

', + 'span-with-inline-css-color-rgb': + '

Hello, World!

', + 'span-with-inline-css-color-rgba': + '

Hello, World!

', + 'span-with-inline-css-backgroundcolor': + '

Hello, World!

', + 'span-with-inline-css-backgroundcolor-rgb': + '

Hello, World!

', + 'span-with-inline-css-backgroundcolor-rgba': + '

Hello, World!

', 'strike': 'Hello, World!', 'strong': 'Hello, World!', 'sub': 'Hello, World!', diff --git a/test/utils_test.dart b/test/utils_test.dart index 571c23af8c..47d532d8a4 100644 --- a/test/utils_test.dart +++ b/test/utils_test.dart @@ -7,7 +7,8 @@ import 'package:flutter_test/flutter_test.dart'; void main() { test('Tests that namedColors returns a valid color', () { - expect(ExpressionMapping.namedColorToColor('red'), equals(ExpressionMapping.stringToColor(namedColors['Red']!))); + expect(ExpressionMapping.namedColorToColor('red'), + equals(ExpressionMapping.stringToColor(namedColors['Red']!))); expect(namedColors['Red'], equals('#FF0000')); }); @@ -26,17 +27,20 @@ void main() { }); test('TextTransformUtil uppercase-s correctly', () { - expect(originalString.transformed(TextTransform.uppercase), equals(uppercaseString)); + expect(originalString.transformed(TextTransform.uppercase), + equals(uppercaseString)); }); test('TextTransformUtil lowercase-s correctly', () { - expect(originalString.transformed(TextTransform.lowercase), equals(lowercaseString)); + expect(originalString.transformed(TextTransform.lowercase), + equals(lowercaseString)); }); const originalLongString = 'Hello, world! pub.dev'; const capitalizedLongString = 'Hello, World! Pub.Dev'; test('TextTransformUtil capitalizes correctly', () { - expect(originalLongString.transformed(TextTransform.capitalize), equals(capitalizedLongString)); + expect(originalLongString.transformed(TextTransform.capitalize), + equals(capitalizedLongString)); }); -} \ No newline at end of file +} From 64f9485b8c168a0b28f856958edd9110b895ae19 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 27 Sep 2022 12:49:33 -0600 Subject: [PATCH 19/82] chore(release): publish packages - flutter_html@3.0.0-alpha.6 - flutter_html_audio@3.0.0-alpha.4 - flutter_html_iframe@3.0.0-alpha.4 - flutter_html_table@3.0.0-alpha.4 - flutter_html_math@3.0.0-alpha.4 - flutter_html_video@3.0.0-alpha.5 - flutter_html_all@3.0.0-alpha.6 - flutter_html_svg@3.0.0-alpha.4 --- CHANGELOG.md | 10 ++++++++++ packages/flutter_html_all/CHANGELOG.md | 4 ++++ packages/flutter_html_all/pubspec.yaml | 16 ++++++++-------- packages/flutter_html_audio/CHANGELOG.md | 4 ++++ packages/flutter_html_audio/pubspec.yaml | 4 ++-- packages/flutter_html_iframe/CHANGELOG.md | 4 ++++ packages/flutter_html_iframe/pubspec.yaml | 4 ++-- packages/flutter_html_math/CHANGELOG.md | 4 ++++ packages/flutter_html_math/pubspec.yaml | 4 ++-- packages/flutter_html_svg/CHANGELOG.md | 4 ++++ packages/flutter_html_svg/pubspec.yaml | 4 ++-- packages/flutter_html_table/CHANGELOG.md | 5 +++++ packages/flutter_html_table/pubspec.yaml | 4 ++-- packages/flutter_html_video/CHANGELOG.md | 4 ++++ packages/flutter_html_video/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- 16 files changed, 60 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44cc2c1cb7..466e16ef84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 3.0.0-alpha.6 + + - **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 diff --git a/packages/flutter_html_all/CHANGELOG.md b/packages/flutter_html_all/CHANGELOG.md index f81d3c94fd..9b53750143 100644 --- a/packages/flutter_html_all/CHANGELOG.md +++ b/packages/flutter_html_all/CHANGELOG.md @@ -1,2 +1,6 @@ +## 3.0.0-alpha.6 + + - Update a dependency to the latest release. + ## [3.0.0-alpha.2] - January 5, 2022: * Initial modularized flutter_html release; use flutter_html_all for full tag support or flutter_html for just the basics diff --git a/packages/flutter_html_all/pubspec.yaml b/packages/flutter_html_all/pubspec.yaml index 82e60c2be3..1dcd3b9910 100644 --- a/packages/flutter_html_all/pubspec.yaml +++ b/packages/flutter_html_all/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_html_all description: All optional flutter_html widgets, bundled into a single package. -version: 3.0.0-alpha.5 +version: 3.0.0-alpha.6 homepage: https://github.com/Sub6Resources/flutter_html environment: @@ -11,13 +11,13 @@ dependencies: flutter: sdk: flutter html: '>=0.15.0 <1.0.0' - flutter_html: '>=3.0.0-alpha.5 <4.0.0' - flutter_html_audio: '>=3.0.0-alpha.3 <4.0.0' - flutter_html_iframe: '>=3.0.0-alpha.3 <4.0.0' - flutter_html_math: '>=3.0.0-alpha.3 <4.0.0' - flutter_html_svg: '>=3.0.0-alpha.3 <4.0.0' - flutter_html_table: '>=3.0.0-alpha.3 <4.0.0' - flutter_html_video: '>=3.0.0-alpha.3 <4.0.0' + flutter_html: ^3.0.0-alpha.6 + flutter_html_audio: ^3.0.0-alpha.4 + flutter_html_iframe: ^3.0.0-alpha.4 + flutter_html_math: ^3.0.0-alpha.4 + flutter_html_svg: ^3.0.0-alpha.4 + flutter_html_table: ^3.0.0-alpha.4 + flutter_html_video: ^3.0.0-alpha.5 # flutter_html_audio: # path: ../flutter_html_audio # flutter_html_iframe: diff --git a/packages/flutter_html_audio/CHANGELOG.md b/packages/flutter_html_audio/CHANGELOG.md index fe3f5ccafe..d0016e519d 100644 --- a/packages/flutter_html_audio/CHANGELOG.md +++ b/packages/flutter_html_audio/CHANGELOG.md @@ -1,2 +1,6 @@ +## 3.0.0-alpha.4 + + - **FIX**: Change CSSBoxWidget to CssBoxWidget. (a62449a7) + ## [3.0.0-alpha.2] - January 5, 2022: * Initial modularized flutter_html release; use flutter_html_audio if you need support for the `
- - - - - - - - - - - - - - - - - - -
OneTwoThree
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
DataData
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, - ), - // 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). - -## 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, 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. 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. - -If you would like to support SVGs in an ``, you should use the [`flutter_html_svg`](#flutter_html_svg) package which provides support for base64, asset, and network SVGs. + - [Style](https://github.com/Sub6Resources/flutter_html/wiki/How-To-Use-Style). + - [HtmlExtension](https://github.com/Sub6Resources/flutter_html/wiki/How-To-Use-Extensions) ## External Packages ### `flutter_html_all` -This package is simply a convenience package that exports all the other external packages below. You should use this if you plan to activate all the renders that require external dependencies. +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. ### `flutter_html_audio` @@ -565,18 +136,20 @@ This package renders audio elements using the [`chewie_audio`](https://pub.dev/p The package considers the attributes `controls`, `loop`, `src`, `autoplay`, `width`, and `muted` when rendering the audio widget. -#### Registering the `CustomRender`: +#### Adding the `AudioHtmlExtension`: Add the dependency to your pubspec.yaml: - dependencies: - flutter_html_audio: ^3.0.0-alpha.3 + flutter pub add flutter_html_audio ```dart +import 'package:flutter_html_audio/flutter_html_audio'; + Widget html = Html( - customRenders: { - audioMatcher(): audioRender(), - } + data: myHtml, + extensions: [ + AudioHtmlExtension(), + ], ); ``` @@ -588,110 +161,84 @@ When rendering iframes, the package considers the width, height, and sandbox att 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`. -#### Registering the `CustomRender`: +#### Adding the `IframeHtmlExtension`: Add the dependency to your pubspec.yaml: - dependencies: - flutter_html_iframe: ^3.0.0-alpha.3 + flutter pub add flutter_html_iframe ```dart -Widget html = Html( - customRenders: { - iframeMatcher(): iframeRender(), - } -); -``` +import 'package:flutter_html_iframe/flutter_html_iframe'; -You can set the `navigationDelegate` of the webview with the `navigationDelegate` property on `iframeRender`. This allows you to block or allow the loading of certain URLs. - -#### `NavigationDelegate` example: - -```dart Widget html = Html( - customRenders: { - iframeMatcher(): iframeRender(navigationDelegate: (NavigationRequest request) { - if (request.url.contains("google.com/images")) { - return NavigationDecision.prevent; - } else { - return NavigationDecision.navigate; - } - }), - } + data: myHtml, + extensions: [ + IframeHtmlExtension(), + ], ); ``` +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. + ### `flutter_html_math` This package renders MathML elements using the [`flutter_math_fork`](https://pub.dev/packages/flutter_math_fork) plugin. 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`. -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. +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. -#### Registering the `CustomRender`: +#### Adding the `MathHtmlExtension`: Add the dependency to your pubspec.yaml: - dependencies: - flutter_html_math: ^3.0.0-alpha.3 + flutter pub add flutter_html_math ```dart +import 'package:flutter_html_math/flutter_html_math'; + Widget html = Html( - customRenders: { - mathMatcher(): mathRender(), - } + data: myHtml, + extensions: [ + MathHtmlExtension(), + ], ); ``` -If the parsing errors, you can use the `onMathError` property of `mathRender` to catch the error and potentially fix it on your end. +If the parsing errors, you can use the `onMathErrorBuilder` property of `MathHtmlException` to catch the error and potentially fix it on your end. The function exposes the parsed Tex `String`, as well as the error and error with type from `flutter_math_fork` as a `String`. You can analyze the error and the parsed string, and finally return a new instance of `Math.tex()` with the corrected Tex string. -#### `onMathError` example: - -```dart -Widget html = Html( - customRenders: { - mathMatcher(): mathRender(onMathError: (tex, exception, exceptionWithType) { - print(exception); - //optionally try and correct the Tex string here - return Text(exception); - }), - } -); -``` - -If you'd like to see more MathML features, feel free to create a PR or file a feature request! - #### Tex 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. Use a custom tag inside your HTML (an example could be ``), and place your **raw** Tex string inside. -Then, use the `customRender` parameter to add the widget to render Tex. It could look like this: +Then, use the `extensions` parameter to add the widget to render Tex. It could look like this: ```dart 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)""", - customRenders: { - texMatcher(): CustomRender.widget(widget: (context, buildChildren) => Math.tex( - context.tree.element?.innerHtml ?? '', - mathStyle: MathStyle.display, - textStyle: context.style.generateTextStyle(), - onErrorFallback: (FlutterMathException e) { - //optionally try and correct the Tex string here - return Text(e.message); - }, - )), - }, - tagsList: Html.tags..add('tex'), + 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); + }, + ); + } + ), + ], ); - -CustomRenderMatcher texMatcher() => (context) => context.tree.element?.localName == 'tex'; ``` ### `flutter_html_svg` @@ -702,21 +249,20 @@ When rendering SVGs, the package takes the SVG data within the `` tag and p The package also exposes a few ways to render SVGs within an `` tag, specifically base64 SVGs, asset SVGs, and network SVGs. -#### Registering the `CustomRender`: +#### Adding the `SvgHtmlExtension`: Add the dependency to your pubspec.yaml: - dependencies: - flutter_html_svg: ^3.0.0-alpha.3 + flutter pub add flutter_html_svg ```dart +import 'package:flutter_html_svg/flutter_html_svg'; + Widget html = Html( - customRenders: { - svgTagMatcher(): svgTagRender(), - svgDataUriMatcher(): svgDataImageRender(), - svgAssetUriMatcher(): svgAssetImageRender(), - svgNetworkSourceMatcher(): svgNetworkImageRender(), - } + data: myHtml, + extensions: [ + SvgHtmlExtension(), + ], ); ``` @@ -726,18 +272,20 @@ This package renders table elements using the [`flutter_layout_grid`](https://pu 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. -#### Registering the `CustomRender`: +#### Adding the `TableHtmlExtension`: Add the dependency to your pubspec.yaml: - dependencies: - flutter_html_table: ^3.0.0-alpha.3 + flutter pub add flutter_html_table ```dart +import 'package:flutter_html_table/flutter_html_table'; + Widget html = Html( - customRenders: { - tableMatcher(): tableRender(), - } + data: myHtml, + extensions: [ + TableHtmlExtension(), + ], ); ``` @@ -747,24 +295,38 @@ This package renders video elements using the [`chewie`](https://pub.dev/package The package considers the attributes `controls`, `loop`, `src`, `autoplay`, `poster`, `width`, `height`, and `muted` when rendering the video widget. -#### Registering the `CustomRender`: +#### Adding the `VideoHtmlExtension`: Add the dependency to your pubspec.yaml: - dependencies: - flutter_html_video: ^3.0.0-alpha.3 + flutter pub add flutter_html_video ```dart +import 'package:flutter_html_video/flutter_html_video'; + Widget html = Html( - customRenders: { - videoMatcher(): videoRender(), - } + data: myHtml, + extensions: [ + VideoHtmlExtension(), + ], ); ``` -## Notes +## FAQ + +### Why can't I get `