From 2bba928ebbd84c83b1c16b425d3d0b80c48ecd33 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Wed, 31 May 2023 08:25:26 -0600 Subject: [PATCH 01/33] Reformat CHANGELOG.md --- CHANGELOG.md | 79 ++-------------------------------------------------- 1 file changed, 2 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 118b64133b..335ac38223 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,81 +1,8 @@ # Change Log -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +#### 3.0.0-beta.2 *May 2023* -## 2023-05-31 - -### Changes - ---- - -Packages with breaking changes: - - - There are no breaking changes in this release. - -Packages with other changes: - - - [`flutter_html` - `v3.0.0-beta.2`](#flutter_html---v300-beta2) - - [`flutter_html_audio` - `v3.0.0-beta.2`](#flutter_html_audio---v300-beta2) - - [`flutter_html_iframe` - `v3.0.0-beta.2`](#flutter_html_iframe---v300-beta2) - - [`flutter_html_math` - `v3.0.0-beta.2`](#flutter_html_math---v300-beta2) - - [`flutter_html_svg` - `v3.0.0-beta.2`](#flutter_html_svg---v300-beta2) - - [`flutter_html_table` - `v3.0.0-beta.2`](#flutter_html_table---v300-beta2) - - [`flutter_html_video` - `v3.0.0-beta.2`](#flutter_html_video---v300-beta2) - - [`flutter_html_all` - `v3.0.0-beta.2`](#flutter_html_all---v300-beta2) - -Packages with dependency updates only: - -> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. - - - `flutter_html_all` - `v3.0.0-beta.2` - ---- - -#### `flutter_html` - `v3.0.0-beta.2` - - - **FIX**: start list items on a new line ([#1281](https://github.com/sub6resources/flutter_html/issues/1281)). ([496d1aa8](https://github.com/sub6resources/flutter_html/commit/496d1aa8e655891d2f597c5e4d7e92057801d815)) - - **FIX**: Add "display: Display.block" to table ([#1278](https://github.com/sub6resources/flutter_html/issues/1278)). ([6350f023](https://github.com/sub6resources/flutter_html/commit/6350f02354b7de601ce294123717e2051be97eee)) - - **FIX**: improve API for ExtensionContext and export marker.dart ([#1273](https://github.com/sub6resources/flutter_html/issues/1273)). ([27e33a95](https://github.com/sub6resources/flutter_html/commit/27e33a955e872d47306db9480f74f6da2e9a028a)) - - **FIX**: Cleaned up whitespace processing and added whitespace tests ([#1267](https://github.com/sub6resources/flutter_html/issues/1267)). ([cc00406b](https://github.com/sub6resources/flutter_html/commit/cc00406b1d0c115e5c66dd4bdfb40db32496f55f)) - - **FIX**: a tag should not style as link if href is not provided ([#1265](https://github.com/sub6resources/flutter_html/issues/1265)). ([d7247cb3](https://github.com/sub6resources/flutter_html/commit/d7247cb303c25d0011f85f9b2d3687924de3d83d)) - - **FEAT**: Update CssBoxWidget to handle rtl marker boxes ([#1270](https://github.com/sub6resources/flutter_html/issues/1270)). ([d7091990](https://github.com/sub6resources/flutter_html/commit/d7091990d193e892e2f782ac8d91fc0326aff4bc)) - - **FEAT**: support vertical-align in inline styles ([#1266](https://github.com/sub6resources/flutter_html/issues/1266)). ([fe896de5](https://github.com/sub6resources/flutter_html/commit/fe896de5ed8b79425bb33800a26fa4ac328057fe)) - - **FEAT**: Add WrapperExtension helper ([#1264](https://github.com/sub6resources/flutter_html/issues/1264)). ([2ffa1dda](https://github.com/sub6resources/flutter_html/commit/2ffa1ddabb3f2a660ab85c551255b89fe8a24ab5)) - -#### `flutter_html_audio` - `v3.0.0-beta.2` - - - **FIX**: improve API for ExtensionContext and export marker.dart ([#1273](https://github.com/sub6resources/flutter_html/issues/1273)). ([27e33a95](https://github.com/sub6resources/flutter_html/commit/27e33a955e872d47306db9480f74f6da2e9a028a)) - - **FEAT**: Add WrapperExtension helper ([#1264](https://github.com/sub6resources/flutter_html/issues/1264)). ([2ffa1dda](https://github.com/sub6resources/flutter_html/commit/2ffa1ddabb3f2a660ab85c551255b89fe8a24ab5)) - -#### `flutter_html_iframe` - `v3.0.0-beta.2` - - - **FIX**: improve API for ExtensionContext and export marker.dart ([#1273](https://github.com/sub6resources/flutter_html/issues/1273)). ([27e33a95](https://github.com/sub6resources/flutter_html/commit/27e33a955e872d47306db9480f74f6da2e9a028a)) - - **FEAT**: Add WrapperExtension helper ([#1264](https://github.com/sub6resources/flutter_html/issues/1264)). ([2ffa1dda](https://github.com/sub6resources/flutter_html/commit/2ffa1ddabb3f2a660ab85c551255b89fe8a24ab5)) - -#### `flutter_html_math` - `v3.0.0-beta.2` - - - **FIX**: improve API for ExtensionContext and export marker.dart ([#1273](https://github.com/sub6resources/flutter_html/issues/1273)). ([27e33a95](https://github.com/sub6resources/flutter_html/commit/27e33a955e872d47306db9480f74f6da2e9a028a)) - - **FEAT**: Add WrapperExtension helper ([#1264](https://github.com/sub6resources/flutter_html/issues/1264)). ([2ffa1dda](https://github.com/sub6resources/flutter_html/commit/2ffa1ddabb3f2a660ab85c551255b89fe8a24ab5)) - -#### `flutter_html_svg` - `v3.0.0-beta.2` - - - **FIX**: improve API for ExtensionContext and export marker.dart ([#1273](https://github.com/sub6resources/flutter_html/issues/1273)). ([27e33a95](https://github.com/sub6resources/flutter_html/commit/27e33a955e872d47306db9480f74f6da2e9a028a)) - - **FEAT**: Add WrapperExtension helper ([#1264](https://github.com/sub6resources/flutter_html/issues/1264)). ([2ffa1dda](https://github.com/sub6resources/flutter_html/commit/2ffa1ddabb3f2a660ab85c551255b89fe8a24ab5)) - -#### `flutter_html_table` - `v3.0.0-beta.2` - - - **FIX**: Add "display: Display.block" to table ([#1278](https://github.com/sub6resources/flutter_html/issues/1278)). ([6350f023](https://github.com/sub6resources/flutter_html/commit/6350f02354b7de601ce294123717e2051be97eee)) - - **FIX**: improve API for ExtensionContext and export marker.dart ([#1273](https://github.com/sub6resources/flutter_html/issues/1273)). ([27e33a95](https://github.com/sub6resources/flutter_html/commit/27e33a955e872d47306db9480f74f6da2e9a028a)) - - **FEAT**: support vertical-align in inline styles ([#1266](https://github.com/sub6resources/flutter_html/issues/1266)). ([fe896de5](https://github.com/sub6resources/flutter_html/commit/fe896de5ed8b79425bb33800a26fa4ac328057fe)) - - **FEAT**: Add WrapperExtension helper ([#1264](https://github.com/sub6resources/flutter_html/issues/1264)). ([2ffa1dda](https://github.com/sub6resources/flutter_html/commit/2ffa1ddabb3f2a660ab85c551255b89fe8a24ab5)) - -#### `flutter_html_video` - `v3.0.0-beta.2` - - - **FIX**: improve API for ExtensionContext and export marker.dart ([#1273](https://github.com/sub6resources/flutter_html/issues/1273)). ([27e33a95](https://github.com/sub6resources/flutter_html/commit/27e33a955e872d47306db9480f74f6da2e9a028a)) - - **FEAT**: Add WrapperExtension helper ([#1264](https://github.com/sub6resources/flutter_html/issues/1264)). ([2ffa1dda](https://github.com/sub6resources/flutter_html/commit/2ffa1ddabb3f2a660ab85c551255b89fe8a24ab5)) - -## 3.0.0-beta.2 + - Several Breaking Changes. See the [migration guide](https://github.com/Sub6Resources/flutter_html/wiki/Migration-Guides#300) - **FIX**: start list items on a new line ([#1281](https://github.com/sub6resources/flutter_html/issues/1281)). ([496d1aa8](https://github.com/sub6resources/flutter_html/commit/496d1aa8e655891d2f597c5e4d7e92057801d815)) - **FIX**: Add "display: Display.block" to table ([#1278](https://github.com/sub6resources/flutter_html/issues/1278)). ([6350f023](https://github.com/sub6resources/flutter_html/commit/6350f02354b7de601ce294123717e2051be97eee)) @@ -86,8 +13,6 @@ Packages with dependency updates only: - **FEAT**: support vertical-align in inline styles ([#1266](https://github.com/sub6resources/flutter_html/issues/1266)). ([fe896de5](https://github.com/sub6resources/flutter_html/commit/fe896de5ed8b79425bb33800a26fa4ac328057fe)) - **FEAT**: Add WrapperExtension helper ([#1264](https://github.com/sub6resources/flutter_html/issues/1264)). ([2ffa1dda](https://github.com/sub6resources/flutter_html/commit/2ffa1ddabb3f2a660ab85c551255b89fe8a24ab5)) -# Change Log - #### 3.0.0-beta.1 - *May 2023* From a55d3df925e39c77f4e63c91a4411bb72ff39a77 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Wed, 31 May 2023 08:54:54 -0600 Subject: [PATCH 02/33] chore: Update upper sdk constraint for external packages to Dart 4.0.0 (#1302) --- packages/flutter_html_all/pubspec.yaml | 2 +- packages/flutter_html_audio/pubspec.yaml | 2 +- packages/flutter_html_iframe/pubspec.yaml | 2 +- packages/flutter_html_math/pubspec.yaml | 2 +- packages/flutter_html_svg/pubspec.yaml | 2 +- packages/flutter_html_table/pubspec.yaml | 2 +- packages/flutter_html_video/pubspec.yaml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/flutter_html_all/pubspec.yaml b/packages/flutter_html_all/pubspec.yaml index f298142146..ae6a082b17 100644 --- a/packages/flutter_html_all/pubspec.yaml +++ b/packages/flutter_html_all/pubspec.yaml @@ -4,7 +4,7 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.12.0 <4.0.0" flutter: ">=2.2.0" dependencies: diff --git a/packages/flutter_html_audio/pubspec.yaml b/packages/flutter_html_audio/pubspec.yaml index 36c354b637..3c414a36f1 100644 --- a/packages/flutter_html_audio/pubspec.yaml +++ b/packages/flutter_html_audio/pubspec.yaml @@ -4,7 +4,7 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.12.0 <4.0.0" flutter: ">=2.2.0" dependencies: diff --git a/packages/flutter_html_iframe/pubspec.yaml b/packages/flutter_html_iframe/pubspec.yaml index 053c84dabc..a9828a2e9c 100644 --- a/packages/flutter_html_iframe/pubspec.yaml +++ b/packages/flutter_html_iframe/pubspec.yaml @@ -4,7 +4,7 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.12.0 <4.0.0" flutter: ">=2.2.0" dependencies: diff --git a/packages/flutter_html_math/pubspec.yaml b/packages/flutter_html_math/pubspec.yaml index 327340e2a2..df968cd0a1 100644 --- a/packages/flutter_html_math/pubspec.yaml +++ b/packages/flutter_html_math/pubspec.yaml @@ -4,7 +4,7 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.12.0 <4.0.0" flutter: ">=2.2.0" dependencies: diff --git a/packages/flutter_html_svg/pubspec.yaml b/packages/flutter_html_svg/pubspec.yaml index e4190f3801..200fc08cb0 100644 --- a/packages/flutter_html_svg/pubspec.yaml +++ b/packages/flutter_html_svg/pubspec.yaml @@ -4,7 +4,7 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.17.0 <3.0.0" + sdk: ">=2.17.0 <4.0.0" flutter: ">=2.2.0" dependencies: diff --git a/packages/flutter_html_table/pubspec.yaml b/packages/flutter_html_table/pubspec.yaml index d07878c801..7a78b11386 100644 --- a/packages/flutter_html_table/pubspec.yaml +++ b/packages/flutter_html_table/pubspec.yaml @@ -5,7 +5,7 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.17.0 <3.0.0" + sdk: ">=2.17.0 <4.0.0" flutter: ">=2.2.0" dependencies: diff --git a/packages/flutter_html_video/pubspec.yaml b/packages/flutter_html_video/pubspec.yaml index 3786624a22..4eb4a7a7c0 100644 --- a/packages/flutter_html_video/pubspec.yaml +++ b/packages/flutter_html_video/pubspec.yaml @@ -4,7 +4,7 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.12.0 <4.0.0" flutter: ">=2.2.0" dependencies: From 32b59d477e8cbf9ae64d13d81c1bb1e09b6f719e Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Wed, 31 May 2023 09:22:12 -0600 Subject: [PATCH 03/33] Update CONTRIBUTING.md --- CONTRIBUTING.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2dc1383211..02466657d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,8 +1,3 @@ Thanks for your interest in contributing to `flutter_html`! -I'm pretty busy, so in order to help me best make use of the time I spend working on this project, please adhere to the following guidelines when contributing: - -1. In general, don't submit a pull request without discussing the feature with me, in an issue, first. I don't want you to have to do a whole bunch of work for nothing. This also makes it so there won't be a whole bunch of changes I make you do in order to have your pull request merged. -2. Please read the [wiki](https://github.com/Sub6Resources/flutter_html/wiki) before contributing (there are only two pages at the moment). This will give you an idea of what my plans are for this repository, and what you do and don't need to work on. - -More specific guidelines will be added soon. +Please see the contribution guide in the wiki: https://github.com/Sub6Resources/flutter_html/wiki/Contributing From 79ec194fa6690e5caba58f5eca04e6748394bb6b Mon Sep 17 00:00:00 2001 From: Little Boy <729049182@qq.com> Date: Mon, 5 Jun 2023 23:43:33 +0800 Subject: [PATCH 04/33] Update flutter math fork version (#1304) --- packages/flutter_html_math/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_html_math/pubspec.yaml b/packages/flutter_html_math/pubspec.yaml index df968cd0a1..375538de19 100644 --- a/packages/flutter_html_math/pubspec.yaml +++ b/packages/flutter_html_math/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: sdk: flutter html: '>=0.15.0 <1.0.0' flutter_html: ^3.0.0-beta.2 - flutter_math_fork: ^0.6.0 + flutter_math_fork: '>=0.6.0 <1.0.0' dev_dependencies: flutter_test: From f229cae85f2d67e85fd24db34fa73012a482fcf0 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Sat, 3 Jun 2023 08:14:03 -0700 Subject: [PATCH 05/33] feat: Expand Display support --- lib/src/style.dart | 41 +++++-- lib/src/style/display.dart | 239 +++++++++++++++++++++++++++++++++++++ 2 files changed, 268 insertions(+), 12 deletions(-) create mode 100644 lib/src/style/display.dart diff --git a/lib/src/style.dart b/lib/src/style.dart index 004b32f4e7..d5d1e0b72d 100644 --- a/lib/src/style.dart +++ b/lib/src/style.dart @@ -5,6 +5,7 @@ import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_html/src/css_parser.dart'; //Export Style value-unit APIs +export 'package:flutter_html/src/style/display.dart'; export 'package:flutter_html/src/style/margin.dart'; export 'package:flutter_html/src/style/padding.dart'; export 'package:flutter_html/src/style/length.dart'; @@ -49,7 +50,7 @@ class Style { /// CSS attribute "`display`" /// /// Inherited: no, - /// Default: unspecified, + /// Default: inline, Display? display; /// CSS attribute "`font-family`" @@ -271,8 +272,7 @@ class Style { this.textOverflow, this.textTransform = TextTransform.none, }) { - if (alignment == null && - (display == Display.block || display == Display.listItem)) { + if (alignment == null && (display?.isBlock ?? false)) { alignment = Alignment.centerLeft; } } @@ -591,14 +591,6 @@ extension MergeBorders on Border? { } } -enum Display { - block, - inline, - inlineBlock, - listItem, - none, -} - enum ListStyleType { arabicIndic('arabic-indic'), armenian('armenian'), @@ -692,7 +684,32 @@ enum VerticalAlign { sup, top, bottom, - middle, + middle; + + /// Converts this [VerticalAlign] to a [PlaceholderAlignment] given the + /// [Display] type of the current context + PlaceholderAlignment toPlaceholderAlignment(Display? display) { + + // vertical-align only applies to inline context elements. + // If we aren't in such a context, use the default 'bottom' alignment. + if(display != Display.inline && display != Display.inlineBlock) { + return PlaceholderAlignment.bottom; + } + + switch(this) { + case VerticalAlign.baseline: + case VerticalAlign.sub: + case VerticalAlign.sup: + return PlaceholderAlignment.baseline; + case VerticalAlign.top: + return PlaceholderAlignment.top; + case VerticalAlign.bottom: + return PlaceholderAlignment.bottom; + case VerticalAlign.middle: + return PlaceholderAlignment.middle; + } + + } } enum WhiteSpace { diff --git a/lib/src/style/display.dart b/lib/src/style/display.dart new file mode 100644 index 0000000000..77914d4c50 --- /dev/null +++ b/lib/src/style/display.dart @@ -0,0 +1,239 @@ +/// Equivalent to CSS `display` +/// +/// (https://www.w3.org/TR/css-display-3/#the-display-properties) +enum Display { + /// Equivalent to css `display: none;` + none ( + displayBox: DisplayBox.none, + ), + + /// Equivalent to css `display: contents;` + /// + /// Not supported by flutter_html + contents ( + displayBox: DisplayBox.contents, + ), + + /// Equivalent to css `display: block;` + block ( + displayOutside: DisplayOutside.block, + displayInside: DisplayInside.flow, + ), + + /// Equivalent to css `display: flow-root;` + /// + /// Not supported by flutter_html + flowRoot ( + displayOutside: DisplayOutside.block, + displayInside: DisplayInside.flowRoot, + ), + + /// Equivalent to css `display: inline;` + inline ( + displayOutside: DisplayOutside.inline, + displayInside: DisplayInside.flow, + ), + + /// Equivalent to css `display: inline-block;` + inlineBlock ( + displayOutside: DisplayOutside.inline, + displayInside: DisplayInside.flowRoot, + ), + + /// Equivalent to css `display: run-in;` + /// + /// Not supported by flutter_html + runIn ( + displayOutside: DisplayOutside.runIn, + displayInside: DisplayInside.flow, + ), + + /// Equivalent to css `display: list-item;` + listItem ( + displayOutside: DisplayOutside.block, + displayInside: DisplayInside.flow, + displayListItem: true, + ), + + /// Equivalent to css `display: inline list-item;` + inlineListItem ( + displayOutside: DisplayOutside.inline, + displayInside: DisplayInside.flow, + displayListItem: true, + ), + + /// Equivalent to css `display: flex;` + /// + /// Not supported by flutter_html + flex ( + displayOutside: DisplayOutside.block, + displayInside: DisplayInside.flex, + ), + + /// Equivalent to css `display: inline-flex;` + /// + /// Not supported by flutter_html + inlineFlex ( + displayOutside: DisplayOutside.inline, + displayInside: DisplayInside.flex, + ), + + /// Equivalent to css `display: grid;` + /// + /// Not supported by flutter_html + grid ( + displayOutside: DisplayOutside.block, + displayInside: DisplayInside.grid, + ), + + /// Equivalent to css `display: inline-grid;` + /// + /// Not supported by flutter_html + inlineGrid ( + displayOutside: DisplayOutside.inline, + displayInside: DisplayInside.grid, + ), + + /// Equivalent to css `display: ruby;` + ruby ( + displayOutside: DisplayOutside.inline, + displayInside: DisplayInside.ruby, + ), + + /// Equivalent to css `display: block ruby;` + blockRuby ( + displayOutside: DisplayOutside.block, + displayInside: DisplayInside.ruby, + ), + + /// Equivalent to css `display: table;` + table ( + displayOutside: DisplayOutside.block, + displayInside: DisplayInside.table, + ), + + /// Equivalent to css `display: inline-table;` + inlineTable ( + displayOutside: DisplayOutside.inline, + displayInside: DisplayInside.table, + ), + + /// Equivalent to css `display: table-row-group;` + tableRowGroup ( + displayInternal: DisplayInternal.tableRowGroup, + ), + + /// Equivalent to css `display: table-header-group;` + tableHeaderGroup ( + displayInternal: DisplayInternal.tableHeaderGroup, + ), + + /// Equivalent to css `display: table-footer-group;` + tableFooterGroup ( + displayInternal: DisplayInternal.tableFooterGroup, + ), + + /// Equivalent to css `display: table-row;` + tableRow ( + displayInternal: DisplayInternal.tableRowGroup, + ), + + /// Equivalent to css `display: table-cell;` + tableCell ( + displayInternal: DisplayInternal.tableCell, + displayInside: DisplayInside.flowRoot, + ), + + /// Equivalent to css `display: table-column-group;` + tableColumnGroup ( + displayInternal: DisplayInternal.tableColumnGroup, + ), + + /// Equivalent to css `display: table-column;` + tableColumn ( + displayInternal: DisplayInternal.tableColumn, + ), + + /// Equivalent to css `display: table-caption;` + tableCaption ( + displayInternal: DisplayInternal.tableCaption, + displayInside: DisplayInside.flowRoot, + ), + + /// Equivalent to css `display: ruby-base;` + rubyBase ( + displayInternal: DisplayInternal.rubyBase, + displayInside: DisplayInside.flow, + ), + + /// Equivalent to css `display: ruby-text;` + rubyText ( + displayInternal: DisplayInternal.rubyText, + displayInside: DisplayInside.flow, + ), + + /// Equivalent to css `display: ruby-base-container;` + rubyBaseContainer ( + displayInternal: DisplayInternal.rubyBaseContainer, + ), + + /// Equivalent to css `display: ruby-text-container;` + rubyTextContainer ( + displayInternal: DisplayInternal.rubyTextContainer, + ); + + const Display({ + this.displayOutside, + this.displayInside, + this.displayListItem = false, + this.displayInternal, + this.displayBox, + }); + + final DisplayOutside? displayOutside; + final DisplayInside? displayInside; + final bool displayListItem; + final DisplayInternal? displayInternal; + final DisplayBox? displayBox; + + /// Evaluates to `true` if `displayOutside` is equal to + /// `DisplayOutside.block`. + bool get isBlock { + return displayOutside == DisplayOutside.block; + } +} + +enum DisplayOutside { + block, + inline, + runIn, // not supported +} + +enum DisplayInside { + flow, + flowRoot, + table, + flex, // not supported + grid, // not supported + ruby, +} + +enum DisplayInternal { + tableRowGroup, + tableHeaderGroup, + tableFooterGroup, + tableRow, + tableCell, + tableColumnGroup, + tableColumn, + tableCaption, + rubyBase, + rubyText, + rubyBaseContainer, + rubyTextContainer, +} + +enum DisplayBox { + contents, // not supported + none, +} \ No newline at end of file From f74c2a57fa6b98957c2e7808da571af991f5d471 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Sat, 3 Jun 2023 08:15:27 -0700 Subject: [PATCH 06/33] fix: Fix many intrinsic issues and add better support for vertical-align throughout --- lib/src/builtins/image_builtin.dart | 2 ++ lib/src/builtins/interactive_element_builtin.dart | 2 ++ lib/src/builtins/styled_element_builtin.dart | 9 +++++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/src/builtins/image_builtin.dart b/lib/src/builtins/image_builtin.dart index 08bfd1b70d..e3e140ee13 100644 --- a/lib/src/builtins/image_builtin.dart +++ b/lib/src/builtins/image_builtin.dart @@ -93,6 +93,8 @@ class ImageBuiltIn extends HtmlExtension { } return WidgetSpan( + alignment: context.style!.verticalAlign.toPlaceholderAlignment(context.style!.display), + baseline: TextBaseline.alphabetic, child: CssBoxWidget( style: imageStyle, childIsReplaced: true, diff --git a/lib/src/builtins/interactive_element_builtin.dart b/lib/src/builtins/interactive_element_builtin.dart index e8486b0701..c22861a086 100644 --- a/lib/src/builtins/interactive_element_builtin.dart +++ b/lib/src/builtins/interactive_element_builtin.dart @@ -67,6 +67,8 @@ class InteractiveElementBuiltIn extends HtmlExtension { ); } else { return WidgetSpan( + alignment: context.style!.verticalAlign.toPlaceholderAlignment(context.style!.display), + baseline: TextBaseline.alphabetic, child: MultipleTapGestureDetector( onTap: onTap, child: GestureDetector( diff --git a/lib/src/builtins/styled_element_builtin.dart b/lib/src/builtins/styled_element_builtin.dart index 67e5b29e61..debb8e8daf 100644 --- a/lib/src/builtins/styled_element_builtin.dart +++ b/lib/src/builtins/styled_element_builtin.dart @@ -427,13 +427,14 @@ class StyledElementBuiltIn extends HtmlExtension { @override InlineSpan build(ExtensionContext context) { - if (context.styledElement!.style.display == Display.listItem || - ((context.styledElement!.style.display == Display.block || - context.styledElement!.style.display == Display.inlineBlock) && + final style = context.styledElement!.style; + final display = style.display; + if (display == Display.listItem || + ((display == Display.block || display == Display.inlineBlock) && (context.styledElement!.children.isNotEmpty || context.elementName == "hr"))) { return WidgetSpan( - alignment: PlaceholderAlignment.baseline, + alignment: style.verticalAlign.toPlaceholderAlignment(display), baseline: TextBaseline.alphabetic, child: CssBoxWidget.withInlineSpanChildren( key: AnchorKey.of(context.parser.key, context.styledElement), From 90550ddeab4eabf4f715d15e56c5a3da82c37373 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 6 Jun 2023 19:26:21 -0700 Subject: [PATCH 07/33] Add fixes for some table and intrinsic sizing issues --- lib/src/builtins/image_builtin.dart | 3 +- .../builtins/interactive_element_builtin.dart | 10 +- lib/src/builtins/styled_element_builtin.dart | 17 +-- lib/src/css_box_widget.dart | 142 +++++++++++------- lib/src/processing/margins.dart | 9 +- lib/src/style.dart | 10 +- lib/src/style/display.dart | 60 ++++---- lib/src/style/margin.dart | 13 ++ .../lib/flutter_html_table.dart | 63 +++++--- test/style/css_parsing/margin_test.dart | 8 +- 10 files changed, 204 insertions(+), 131 deletions(-) diff --git a/lib/src/builtins/image_builtin.dart b/lib/src/builtins/image_builtin.dart index e3e140ee13..a94ee2a71a 100644 --- a/lib/src/builtins/image_builtin.dart +++ b/lib/src/builtins/image_builtin.dart @@ -93,7 +93,8 @@ class ImageBuiltIn extends HtmlExtension { } return WidgetSpan( - alignment: context.style!.verticalAlign.toPlaceholderAlignment(context.style!.display), + alignment: context.style!.verticalAlign + .toPlaceholderAlignment(context.style!.display), baseline: TextBaseline.alphabetic, child: CssBoxWidget( style: imageStyle, diff --git a/lib/src/builtins/interactive_element_builtin.dart b/lib/src/builtins/interactive_element_builtin.dart index c22861a086..f2537b9cd3 100644 --- a/lib/src/builtins/interactive_element_builtin.dart +++ b/lib/src/builtins/interactive_element_builtin.dart @@ -61,13 +61,19 @@ class InteractiveElementBuiltIn extends HtmlExtension { children: childSpan.children ?.map((e) => _processInteractableChild(context, e)) .toList(), + recognizer: TapGestureRecognizer()..onTap = onTap, style: childSpan.style, semanticsLabel: childSpan.semanticsLabel, - recognizer: TapGestureRecognizer()..onTap = onTap, + locale: childSpan.locale, + mouseCursor: childSpan.mouseCursor, + onEnter: childSpan.onEnter, + onExit: childSpan.onExit, + spellOut: childSpan.spellOut, ); } else { return WidgetSpan( - alignment: context.style!.verticalAlign.toPlaceholderAlignment(context.style!.display), + alignment: context.style!.verticalAlign + .toPlaceholderAlignment(context.style!.display), baseline: TextBaseline.alphabetic, child: MultipleTapGestureDetector( onTap: onTap, diff --git a/lib/src/builtins/styled_element_builtin.dart b/lib/src/builtins/styled_element_builtin.dart index debb8e8daf..a750c93187 100644 --- a/lib/src/builtins/styled_element_builtin.dart +++ b/lib/src/builtins/styled_element_builtin.dart @@ -428,9 +428,9 @@ class StyledElementBuiltIn extends HtmlExtension { @override InlineSpan build(ExtensionContext context) { final style = context.styledElement!.style; - final display = style.display; - if (display == Display.listItem || - ((display == Display.block || display == Display.inlineBlock) && + final display = style.display ?? Display.inline; + if (display.displayListItem || + ((display.isBlock || display == Display.inlineBlock) && (context.styledElement!.children.isNotEmpty || context.elementName == "hr"))) { return WidgetSpan( @@ -438,17 +438,16 @@ class StyledElementBuiltIn extends HtmlExtension { baseline: TextBaseline.alphabetic, child: CssBoxWidget.withInlineSpanChildren( key: AnchorKey.of(context.parser.key, context.styledElement), - style: context.styledElement!.style, + style: context.style!, shrinkWrap: context.parser.shrinkWrap, - childIsReplaced: ["iframe", "img", "video", "audio"] - .contains(context.styledElement!.name), + childIsReplaced: + ["iframe", "img", "video", "audio"].contains(context.elementName), children: context.builtChildrenMap!.entries .expandIndexed((i, child) => [ child.value, if (context.parser.shrinkWrap && i != context.styledElement!.children.length - 1 && - (child.key.style.display == Display.block || - child.key.style.display == Display.listItem) && + (child.key.style.display?.isBlock ?? false) && child.key.element?.localName != "html" && child.key.element?.localName != "body") const TextSpan(text: "\n", style: TextStyle(fontSize: 0)), @@ -464,7 +463,7 @@ class StyledElementBuiltIn extends HtmlExtension { .expandIndexed((index, child) => [ child.value, if (context.parser.shrinkWrap && - child.key.style.display == Display.block && + (child.key.style.display?.isBlock ?? false) && index != context.styledElement!.children.length - 1 && child.key.element?.parent?.localName != "th" && child.key.element?.parent?.localName != "td" && diff --git a/lib/src/css_box_widget.dart b/lib/src/css_box_widget.dart index 85679ea4b7..f907b323e5 100644 --- a/lib/src/css_box_widget.dart +++ b/lib/src/css_box_widget.dart @@ -159,10 +159,7 @@ 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.listItem) && - !childIsReplaced && - !shrinkWrap; + return (style.display?.isBlock ?? false) && !childIsReplaced && !shrinkWrap; } TextDirection _checkTextDirection( @@ -424,42 +421,62 @@ class RenderCSSBox extends RenderBox } } - static double getIntrinsicDimension(RenderBox? firstChild, - double Function(RenderBox child) mainChildSizeGetter) { + static double getIntrinsicDimension( + RenderBox? firstChild, + double Function(RenderBox child) mainChildSizeGetter, + double marginSpaceNeeded) { double extent = 0.0; RenderBox? child = firstChild; while (child != null) { final CSSBoxParentData childParentData = child.parentData! as CSSBoxParentData; - extent = math.max(extent, mainChildSizeGetter(child)); + try { + extent = math.max(extent, mainChildSizeGetter(child)); + } catch (_) { + // See https://github.com/flutter/flutter/issues/65895 + debugPrint( + "Due to Flutter layout restrictions (see https://github.com/flutter/flutter/issues/65895), contents set to `vertical-align: baseline` within an intrinsically-sized layout may not display as expected. If content is cut off or displaying incorrectly, please try setting vertical-align to 'bottom' on the problematic elements"); + } assert(child.parentData == childParentData); child = childParentData.nextSibling; } - return extent; + return extent + marginSpaceNeeded; } @override double computeMinIntrinsicWidth(double height) { return getIntrinsicDimension( - firstChild, (RenderBox child) => child.getMinIntrinsicWidth(height)); + firstChild, + (RenderBox child) => child.getMinIntrinsicWidth(height), + _calculateIntrinsicMargins().horizontal, + ); } @override double computeMaxIntrinsicWidth(double height) { return getIntrinsicDimension( - firstChild, (RenderBox child) => child.getMaxIntrinsicWidth(height)); + firstChild, + (RenderBox child) => child.getMaxIntrinsicWidth(height), + _calculateIntrinsicMargins().horizontal, + ); } @override double computeMinIntrinsicHeight(double width) { return getIntrinsicDimension( - firstChild, (RenderBox child) => child.getMinIntrinsicHeight(width)); + firstChild, + (RenderBox child) => child.getMinIntrinsicHeight(width), + _calculateIntrinsicMargins().vertical, + ); } @override double computeMaxIntrinsicHeight(double width) { return getIntrinsicDimension( - firstChild, (RenderBox child) => child.getMaxIntrinsicHeight(width)); + firstChild, + (RenderBox child) => child.getMaxIntrinsicHeight(width), + _calculateIntrinsicMargins().vertical, + ); } @override @@ -521,31 +538,20 @@ class RenderCSSBox extends RenderBox //Calculate Width and Height of CSS Box height = childSize.height; - switch (display) { - case Display.block: - width = (shrinkWrap || childIsReplaced) - ? childSize.width + horizontalMargins - : containingBlockSize.width; - height = childSize.height + verticalMargins; - break; - case Display.inline: - width = childSize.width + horizontalMargins; - height = childSize.height; - break; - case Display.inlineBlock: - width = childSize.width + horizontalMargins; - height = childSize.height + verticalMargins; - break; - case Display.listItem: - width = shrinkWrap - ? childSize.width + horizontalMargins - : containingBlockSize.width; - height = childSize.height + verticalMargins; - break; - case Display.none: - width = 0; - height = 0; - break; + if (display.displayBox == DisplayBox.none) { + width = 0; + height = 0; + } else if (display == Display.inlineBlock) { + width = childSize.width + horizontalMargins; + height = childSize.height + verticalMargins; + } else if (display.isBlock) { + width = (shrinkWrap || childIsReplaced) + ? childSize.width + horizontalMargins + : containingBlockSize.width; + height = childSize.height + verticalMargins; + } else { + width = childSize.width + horizontalMargins; + height = childSize.height; } return _Sizes(constraints.constrain(Size(width, height)), childSize); @@ -575,26 +581,14 @@ class RenderCSSBox extends RenderBox double leftOffset = 0; double topOffset = 0; - switch (display) { - case Display.block: - leftOffset = leftMargin; - topOffset = topMargin; - break; - case Display.inline: - leftOffset = leftMargin; - break; - case Display.inlineBlock: - leftOffset = leftMargin; - topOffset = topMargin; - break; - case Display.listItem: - leftOffset = leftMargin; - topOffset = topMargin; - break; - case Display.none: - //No offset - break; + + if (display.isBlock || display == Display.inlineBlock) { + leftOffset = leftMargin; + topOffset = topMargin; + } else if (display.displayOutside == DisplayOutside.inline) { + leftOffset = leftMargin; } + childParentData.offset = Offset(leftOffset, topOffset); assert(child.parentData == childParentData); @@ -628,7 +622,7 @@ class RenderCSSBox extends RenderBox Margins _calculateUsedMargins(Size childSize, Size containingBlockSize) { //We assume that margins have already been preprocessed - // (i.e. they are non-null and either px units or auto. + // (i.e. they are non-null and either px units or auto). assert(margins.left != null && margins.right != null); assert(margins.left!.unit == Unit.px || margins.left!.unit == Unit.auto); assert(margins.right!.unit == Unit.px || margins.right!.unit == Unit.auto); @@ -737,6 +731,40 @@ class RenderCSSBox extends RenderBox ); } + Margins _calculateIntrinsicMargins() { + //We assume that margins have already been preprocessed + // (i.e. they are non-null and either px units or auto). + assert(margins.left != null && margins.right != null); + assert(margins.left!.unit == Unit.px || margins.left!.unit == Unit.auto); + assert(margins.right!.unit == Unit.px || margins.right!.unit == Unit.auto); + + Margin marginLeft = margins.left!; + Margin marginRight = margins.right!; + + bool marginLeftIsAuto = marginLeft.unit == Unit.auto; + bool marginRightIsAuto = marginRight.unit == Unit.auto; + + if (display.isBlock) { + if (marginLeftIsAuto) { + marginLeft = Margin(0); + } + + if (marginRightIsAuto) { + marginRight = Margin(0); + } + } else { + marginLeft = Margin(0); + marginRight = Margin(0); + } + + return Margins( + left: marginLeft, + right: marginRight, + top: margins.top, + bottom: margins.bottom, + ); + } + @override bool hitTestChildren(BoxHitTestResult result, {required Offset position}) { return defaultHitTestChildren(result, position: position); diff --git a/lib/src/processing/margins.dart b/lib/src/processing/margins.dart index fb1378912c..2598fcb096 100644 --- a/lib/src/processing/margins.dart +++ b/lib/src/processing/margins.dart @@ -32,8 +32,10 @@ class MarginProcessing { //Collapsing should be depth-first. tree.children.forEach(_collapseMargins); - //The root boxes do not collapse. - if (tree.name == '[Tree Root]' || tree.name == 'html') { + //The root boxes and table/ruby elements do not collapse. + if (tree.name == '[Tree Root]' || + tree.name == 'html' || + tree.style.display?.displayInternal != null) { return tree; } @@ -67,7 +69,8 @@ class MarginProcessing { // Handle case (3) from above. // Bottom margins cannot collapse if the element has padding - if ((tree.style.padding?.bottom ?? tree.style.padding?.blockEnd ?? 0) == + if ((tree.style.padding?.bottom?.value ?? + tree.style.padding?.blockEnd?.value) == 0) { final parentBottom = tree.style.margin?.bottom?.value ?? tree.style.margin?.blockEnd?.value ?? diff --git a/lib/src/style.dart b/lib/src/style.dart index d5d1e0b72d..059bcb4e3f 100644 --- a/lib/src/style.dart +++ b/lib/src/style.dart @@ -689,14 +689,17 @@ enum VerticalAlign { /// Converts this [VerticalAlign] to a [PlaceholderAlignment] given the /// [Display] type of the current context PlaceholderAlignment toPlaceholderAlignment(Display? display) { - // vertical-align only applies to inline context elements. // If we aren't in such a context, use the default 'bottom' alignment. - if(display != Display.inline && display != Display.inlineBlock) { + // Also note that the default display, if it is not set, is inline, so we + // treat null `display` values as if they were inline by default. + if (display != Display.inline && + display != Display.inlineBlock && + display != null) { return PlaceholderAlignment.bottom; } - switch(this) { + switch (this) { case VerticalAlign.baseline: case VerticalAlign.sub: case VerticalAlign.sup: @@ -708,7 +711,6 @@ enum VerticalAlign { case VerticalAlign.middle: return PlaceholderAlignment.middle; } - } } diff --git a/lib/src/style/display.dart b/lib/src/style/display.dart index 77914d4c50..b69c2100c9 100644 --- a/lib/src/style/display.dart +++ b/lib/src/style/display.dart @@ -3,19 +3,19 @@ /// (https://www.w3.org/TR/css-display-3/#the-display-properties) enum Display { /// Equivalent to css `display: none;` - none ( + none( displayBox: DisplayBox.none, ), /// Equivalent to css `display: contents;` /// /// Not supported by flutter_html - contents ( + contents( displayBox: DisplayBox.contents, ), /// Equivalent to css `display: block;` - block ( + block( displayOutside: DisplayOutside.block, displayInside: DisplayInside.flow, ), @@ -23,19 +23,19 @@ enum Display { /// Equivalent to css `display: flow-root;` /// /// Not supported by flutter_html - flowRoot ( + flowRoot( displayOutside: DisplayOutside.block, displayInside: DisplayInside.flowRoot, ), /// Equivalent to css `display: inline;` - inline ( + inline( displayOutside: DisplayOutside.inline, displayInside: DisplayInside.flow, ), /// Equivalent to css `display: inline-block;` - inlineBlock ( + inlineBlock( displayOutside: DisplayOutside.inline, displayInside: DisplayInside.flowRoot, ), @@ -43,20 +43,20 @@ enum Display { /// Equivalent to css `display: run-in;` /// /// Not supported by flutter_html - runIn ( + runIn( displayOutside: DisplayOutside.runIn, displayInside: DisplayInside.flow, ), /// Equivalent to css `display: list-item;` - listItem ( + listItem( displayOutside: DisplayOutside.block, displayInside: DisplayInside.flow, displayListItem: true, ), /// Equivalent to css `display: inline list-item;` - inlineListItem ( + inlineListItem( displayOutside: DisplayOutside.inline, displayInside: DisplayInside.flow, displayListItem: true, @@ -65,7 +65,7 @@ enum Display { /// Equivalent to css `display: flex;` /// /// Not supported by flutter_html - flex ( + flex( displayOutside: DisplayOutside.block, displayInside: DisplayInside.flex, ), @@ -73,7 +73,7 @@ enum Display { /// Equivalent to css `display: inline-flex;` /// /// Not supported by flutter_html - inlineFlex ( + inlineFlex( displayOutside: DisplayOutside.inline, displayInside: DisplayInside.flex, ), @@ -81,7 +81,7 @@ enum Display { /// Equivalent to css `display: grid;` /// /// Not supported by flutter_html - grid ( + grid( displayOutside: DisplayOutside.block, displayInside: DisplayInside.grid, ), @@ -89,96 +89,96 @@ enum Display { /// Equivalent to css `display: inline-grid;` /// /// Not supported by flutter_html - inlineGrid ( + inlineGrid( displayOutside: DisplayOutside.inline, displayInside: DisplayInside.grid, ), /// Equivalent to css `display: ruby;` - ruby ( + ruby( displayOutside: DisplayOutside.inline, displayInside: DisplayInside.ruby, ), /// Equivalent to css `display: block ruby;` - blockRuby ( + blockRuby( displayOutside: DisplayOutside.block, displayInside: DisplayInside.ruby, ), /// Equivalent to css `display: table;` - table ( + table( displayOutside: DisplayOutside.block, displayInside: DisplayInside.table, ), /// Equivalent to css `display: inline-table;` - inlineTable ( + inlineTable( displayOutside: DisplayOutside.inline, displayInside: DisplayInside.table, ), /// Equivalent to css `display: table-row-group;` - tableRowGroup ( + tableRowGroup( displayInternal: DisplayInternal.tableRowGroup, ), /// Equivalent to css `display: table-header-group;` - tableHeaderGroup ( + tableHeaderGroup( displayInternal: DisplayInternal.tableHeaderGroup, ), /// Equivalent to css `display: table-footer-group;` - tableFooterGroup ( + tableFooterGroup( displayInternal: DisplayInternal.tableFooterGroup, ), /// Equivalent to css `display: table-row;` - tableRow ( + tableRow( displayInternal: DisplayInternal.tableRowGroup, ), /// Equivalent to css `display: table-cell;` - tableCell ( + tableCell( displayInternal: DisplayInternal.tableCell, displayInside: DisplayInside.flowRoot, ), /// Equivalent to css `display: table-column-group;` - tableColumnGroup ( + tableColumnGroup( displayInternal: DisplayInternal.tableColumnGroup, ), /// Equivalent to css `display: table-column;` - tableColumn ( + tableColumn( displayInternal: DisplayInternal.tableColumn, ), /// Equivalent to css `display: table-caption;` - tableCaption ( + tableCaption( displayInternal: DisplayInternal.tableCaption, displayInside: DisplayInside.flowRoot, ), /// Equivalent to css `display: ruby-base;` - rubyBase ( + rubyBase( displayInternal: DisplayInternal.rubyBase, displayInside: DisplayInside.flow, ), /// Equivalent to css `display: ruby-text;` - rubyText ( + rubyText( displayInternal: DisplayInternal.rubyText, displayInside: DisplayInside.flow, ), /// Equivalent to css `display: ruby-base-container;` - rubyBaseContainer ( + rubyBaseContainer( displayInternal: DisplayInternal.rubyBaseContainer, ), /// Equivalent to css `display: ruby-text-container;` - rubyTextContainer ( + rubyTextContainer( displayInternal: DisplayInternal.rubyTextContainer, ); @@ -236,4 +236,4 @@ enum DisplayInternal { enum DisplayBox { contents, // not supported none, -} \ No newline at end of file +} diff --git a/lib/src/style/margin.dart b/lib/src/style/margin.dart index d2cbe157d1..257fa0b296 100644 --- a/lib/src/style/margin.dart +++ b/lib/src/style/margin.dart @@ -63,6 +63,19 @@ class Margins { blockStart?.unit == Unit.auto ? blockStart : Margin(0, Unit.px)); } + /// The total margin in the horizontal direction. + double get horizontal => + (left?.value ?? inlineStart?.value ?? 0) + + (right?.value ?? inlineEnd?.value ?? 0); + + /// The total margin in the vertical direction. + double get vertical => + (top?.value ?? blockStart?.value ?? 0) + + (bottom?.value ?? blockEnd?.value ?? 0); + + /// The size that this [Margins] would occupy with an empty interior. + Size get collapsedSize => Size(horizontal, vertical); + Margins copyWith({ Margin? left, Margin? right, diff --git a/packages/flutter_html_table/lib/flutter_html_table.dart b/packages/flutter_html_table/lib/flutter_html_table.dart index f7e928c6c7..0ec6607b39 100644 --- a/packages/flutter_html_table/lib/flutter_html_table.dart +++ b/packages/flutter_html_table/lib/flutter_html_table.dart @@ -39,7 +39,7 @@ class TableHtmlExtension extends HtmlExtension { elementClasses: context.classes.toList(), tableStructure: children, cellDescendants: cellDescendants, - style: Style(display: Display.block), + style: Style(display: Display.table), node: context.node, ); } @@ -51,9 +51,11 @@ class TableHtmlExtension extends HtmlExtension { fontWeight: FontWeight.bold, textAlign: TextAlign.center, verticalAlign: VerticalAlign.middle, + display: Display.tableCell, ) : Style( verticalAlign: VerticalAlign.middle, + display: Display.tableCell, ), children: children, node: context.node, @@ -71,7 +73,13 @@ class TableHtmlExtension extends HtmlExtension { elementId: context.id, elementClasses: context.classes.toList(), children: children, - style: Style(), + style: Style( + display: context.elementName == "thead" + ? Display.tableHeaderGroup + : context.elementName == "tfoot" + ? Display.tableFooterGroup + : Display.tableRowGroup, + ), node: context.node, ); } @@ -82,7 +90,9 @@ class TableHtmlExtension extends HtmlExtension { elementId: context.id, elementClasses: context.classes.toList(), children: children, - style: Style(), + style: Style( + display: Display.tableRow, + ), node: context.node, ); } @@ -93,7 +103,11 @@ class TableHtmlExtension extends HtmlExtension { elementId: context.id, elementClasses: context.classes.toList(), children: children, - style: Style(), + style: Style( + display: context.elementName == "col" + ? Display.tableColumn + : Display.tableColumnGroup, + ), node: context.node, ); } @@ -106,7 +120,7 @@ class TableHtmlExtension extends HtmlExtension { if (context.elementName == "table") { return WidgetSpan( child: CssBoxWidget( - style: context.styledElement!.style, + style: context.style!, child: LayoutBuilder( builder: (_, constraints) { return _layoutCells( @@ -125,6 +139,7 @@ class TableHtmlExtension extends HtmlExtension { child: CssBoxWidget.withInlineSpanChildren( children: context.inlineSpanChildren!, style: Style(), + childIsReplaced: true, ), ); } @@ -231,28 +246,31 @@ Widget _layoutCells( columni += columnColspanOffset[columni].clamp(1, columnMax - columni - 1); } + + final colspan = min(child.colspan, columnMax - columni); + final rowspan = min(child.rowspan, rows.length - rowi); + cells.add(GridPlacement( columnStart: columni, - columnSpan: min(child.colspan, columnMax - columni), + columnSpan: colspan, rowStart: rowi, - rowSpan: min(child.rowspan, rows.length - rowi), + rowSpan: rowspan, child: CssBoxWidget( style: child.style.merge(row.style), - child: Builder(builder: (context) { - final alignment = - child.style.direction ?? Directionality.of(context); - return SizedBox.expand( - child: Container( - alignment: _getCellAlignment(child, alignment), - child: CssBoxWidget.withInlineSpanChildren( - children: [ - parsedCells[child] ?? const TextSpan(text: "error") - ], - style: Style(), - ), + child: SizedBox.expand( + child: Container( + alignment: _getCellAlignment( + child, + child.style.direction ?? + Directionality.of(context.buildContext!)), + child: CssBoxWidget.withInlineSpanChildren( + children: [ + parsedCells[child] ?? const TextSpan(text: "error") + ], + style: Style(), ), - ); - }), + ), + ), ), )); columnRowOffset[columni] = child.rowspan - 1; @@ -281,6 +299,9 @@ Widget _layoutCells( gridFit: GridFit.loose, columnSizes: finalColumnSizes, rowSizes: rowSizes, + // TODO add style option for border-spacing + // rowGap: 2, + // columnGap: 2, children: cells, ); } diff --git a/test/style/css_parsing/margin_test.dart b/test/style/css_parsing/margin_test.dart index 0890cc8e98..67728bff5c 100644 --- a/test/style/css_parsing/margin_test.dart +++ b/test/style/css_parsing/margin_test.dart @@ -156,8 +156,8 @@ void main() { ), ), ); - expect(_getMargin("Test"), - equals(Margins.only(bottom: 8, blockEnd: 8, unit: Unit.px))); + expect( + _getMargin("Test"), equals(Margins.only(blockEnd: 8, unit: Unit.px))); }, ); @@ -291,8 +291,8 @@ void main() { ), ), ); - expect(_getMargin("Test"), - equals(Margins.only(bottom: 8, blockEnd: 8, unit: Unit.px))); + expect( + _getMargin("Test"), equals(Margins.only(blockEnd: 8, unit: Unit.px))); }, ); From 4ffbde961cee2355393c170201c73aa4efaf5af4 Mon Sep 17 00:00:00 2001 From: Kaique Gazola Date: Tue, 4 Jul 2023 18:05:19 -0300 Subject: [PATCH 08/33] feat: expose image element for custom render / extensions --- lib/flutter_html.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/flutter_html.dart b/lib/flutter_html.dart index 0247d4c94b..d51a5f8a1f 100644 --- a/lib/flutter_html.dart +++ b/lib/flutter_html.dart @@ -10,6 +10,7 @@ import 'package:html/dom.dart' as dom; export 'package:flutter_html/src/html_parser.dart'; //export src for advanced custom render uses (e.g. casting context.tree) export 'package:flutter_html/src/anchor.dart'; +export 'package:flutter_html/src/tree/image_element.dart'; export 'package:flutter_html/src/tree/interactable_element.dart'; export 'package:flutter_html/src/tree/replaced_element.dart'; export 'package:flutter_html/src/tree/styled_element.dart'; From cd0992f621aa20c27b061cbfef7fe1639a67a8e7 Mon Sep 17 00:00:00 2001 From: Kaique Gazola Date: Tue, 4 Jul 2023 19:04:10 -0300 Subject: [PATCH 09/33] refactor: remove unused image element imports --- lib/src/builtins/image_builtin.dart | 1 - packages/flutter_html_svg/lib/flutter_html_svg.dart | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/src/builtins/image_builtin.dart b/lib/src/builtins/image_builtin.dart index 08bfd1b70d..15c4bbc3f9 100644 --- a/lib/src/builtins/image_builtin.dart +++ b/lib/src/builtins/image_builtin.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; -import 'package:flutter_html/src/tree/image_element.dart'; class ImageBuiltIn extends HtmlExtension { final String? dataEncoding; diff --git a/packages/flutter_html_svg/lib/flutter_html_svg.dart b/packages/flutter_html_svg/lib/flutter_html_svg.dart index 93dfa849f9..fe31364d10 100644 --- a/packages/flutter_html_svg/lib/flutter_html_svg.dart +++ b/packages/flutter_html_svg/lib/flutter_html_svg.dart @@ -1,10 +1,9 @@ library flutter_html_svg; import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; -// ignore: implementation_imports -import 'package:flutter_html/src/tree/image_element.dart'; import 'package:flutter_svg/flutter_svg.dart'; // TODO re-add MultipleGestureDetector for image taps in this extension From 6d90bd77412daf9d51cb364b20f19459cfff5349 Mon Sep 17 00:00:00 2001 From: Nicolas Dusart Date: Wed, 19 Jul 2023 18:02:16 +0200 Subject: [PATCH 10/33] update readme for subpackages for migration from CustomRender to HtmlExtension --- packages/flutter_html_audio/README.md | 8 ++++---- packages/flutter_html_iframe/README.md | 12 ++++++------ packages/flutter_html_math/README.md | 14 +++++++------- packages/flutter_html_svg/README.md | 9 +++------ packages/flutter_html_table/README.md | 6 +++--- packages/flutter_html_video/README.md | 8 ++++---- 6 files changed, 27 insertions(+), 30 deletions(-) diff --git a/packages/flutter_html_audio/README.md b/packages/flutter_html_audio/README.md index 81ac741006..16f4f6eeaa 100644 --- a/packages/flutter_html_audio/README.md +++ b/packages/flutter_html_audio/README.md @@ -6,12 +6,12 @@ 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`: +#### Registering the `AudioHtmlExtension`: ```dart Widget html = Html( - customRenders: { - audioMatcher(): audioRender(), + extensions: { + AudioHtmlExtension(), } ); -``` \ No newline at end of file +``` diff --git a/packages/flutter_html_iframe/README.md b/packages/flutter_html_iframe/README.md index 2c46edc0e7..facb70174e 100644 --- a/packages/flutter_html_iframe/README.md +++ b/packages/flutter_html_iframe/README.md @@ -8,23 +8,23 @@ 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`: +#### Registering the `IframeHtmlExtension`: ```dart Widget html = Html( - customRenders: { - iframeMatcher(): iframeRender(), + extensions: { + IframeHtmlExtension(), } ); ``` -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. +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. #### `NavigationDelegate` example: ```dart Widget html = Html( - customRenders: { - iframeMatcher(): iframeRender(navigationDelegate: (NavigationRequest request) { + extensions: { + IframeHtmlExtension(navigationDelegate: (NavigationRequest request) { if (request.url.contains("google.com/images")) { return NavigationDecision.prevent; } else { diff --git a/packages/flutter_html_math/README.md b/packages/flutter_html_math/README.md index 4dd30c1da1..5f4817a504 100644 --- a/packages/flutter_html_math/README.md +++ b/packages/flutter_html_math/README.md @@ -8,17 +8,17 @@ When rendering MathML, the package takes the MathML data within the `` tag 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. -#### Registering the `CustomRender`: +#### Registering the `MathHtmlExtension`: ```dart Widget html = Html( - customRenders: { - mathMatcher(): mathRender(), + 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 `onMathError` property of `MathHtmlExtension` 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`. @@ -28,12 +28,12 @@ You can analyze the error and the parsed string, and finally return a new instan ```dart Widget html = Html( - customRenders: { - mathMatcher(): mathRender(onMathError: (tex, exception, exceptionWithType) { + extensions: { + MathHtmlExtension(onMathError: (tex, exception, exceptionWithType) { print(exception); //optionally try and correct the Tex string here return Text(exception); }), } ); -``` \ No newline at end of file +``` diff --git a/packages/flutter_html_svg/README.md b/packages/flutter_html_svg/README.md index 712da67ba7..6610e6e73f 100644 --- a/packages/flutter_html_svg/README.md +++ b/packages/flutter_html_svg/README.md @@ -8,15 +8,12 @@ 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`: +#### Registering the `SvgHtmlExtension`: ```dart Widget html = Html( - customRenders: { - svgTagMatcher(): svgTagRender(), - svgDataUriMatcher(): svgDataImageRender(), - svgAssetUriMatcher(): svgAssetImageRender(), - svgNetworkSourceMatcher(): svgNetworkImageRender(), + extensions: { + SvgHtmlExtension(), } ); ``` diff --git a/packages/flutter_html_table/README.md b/packages/flutter_html_table/README.md index f2ef51789b..87b21e5539 100644 --- a/packages/flutter_html_table/README.md +++ b/packages/flutter_html_table/README.md @@ -6,12 +6,12 @@ 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`: +#### Registering the `TableHtmlExtension`: ```dart Widget html = Html( - customRenders: { - tableMatcher(): tableRender(), + extensions: { + TableHtmlExtension(), } ); ``` diff --git a/packages/flutter_html_video/README.md b/packages/flutter_html_video/README.md index a4d0644a4c..e48255229d 100644 --- a/packages/flutter_html_video/README.md +++ b/packages/flutter_html_video/README.md @@ -6,12 +6,12 @@ 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`: +#### Registering the `VideoHtmlExtension`: ```dart Widget html = Html( - customRenders: { - videoMatcher(): videoRender(), + extensions: { + VideoHtmlExtension(), } ); -``` \ No newline at end of file +``` From 403a644a98de25555f4f3f6050e76ef07c66ea2b Mon Sep 17 00:00:00 2001 From: Mark Fransen Date: Wed, 9 Aug 2023 16:10:14 -0700 Subject: [PATCH 11/33] Add srcdoc check when building IframeWidget This branch allows developers to, in addition to 'src', pass in html that contains a 'srcdoc' property. 'src' is great for passing in a URL, but if someone wants to pass in HTML, etc., inside an iframe, then we need to use 'srcdoc'. This branch is broken into three commits since there are three separate features being introduced. Check is 'srcdoc' is sent in with the extensionContext attributes. If there is no 'srcdoc' we fallback to 'src'. This change doesn't break the current usage of the package and will not impact developers currently using it. --- .../lib/iframe_mobile.dart | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/flutter_html_iframe/lib/iframe_mobile.dart b/packages/flutter_html_iframe/lib/iframe_mobile.dart index 9c73e65cba..7ef80489b0 100644 --- a/packages/flutter_html_iframe/lib/iframe_mobile.dart +++ b/packages/flutter_html_iframe/lib/iframe_mobile.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -34,6 +36,18 @@ class IframeWidget extends StatelessWidget { final givenHeight = double.tryParse(extensionContext.attributes['height'] ?? ""); + Uri? srcUri; + + if (extensionContext.attributes['srcdoc'] != null) { + srcUri = Uri.dataFromString( + extensionContext.attributes['srcdoc'] ?? '', + mimeType: 'text/html', + encoding: Encoding.getByName('utf-8'), + ); + } else { + srcUri = Uri.tryParse(extensionContext.attributes['src'] ?? "") ?? Uri(); + } + return SizedBox( width: givenWidth ?? (givenHeight ?? 150) * 2, height: givenHeight ?? (givenWidth ?? 300) / 2, @@ -41,10 +55,7 @@ class IframeWidget extends StatelessWidget { style: extensionContext.styledElement!.style, childIsReplaced: true, child: WebViewWidget( - controller: controller - ..loadRequest( - Uri.tryParse(extensionContext.attributes['src'] ?? "") ?? - Uri()), + controller: controller..loadRequest(srcUri), key: key, gestureRecognizers: {Factory(() => VerticalDragGestureRecognizer())}, ), From 4dc81e179ea68414077e18678c08e501d01cc406 Mon Sep 17 00:00:00 2001 From: Gabriel Araujo Date: Mon, 6 Nov 2023 14:45:49 -0300 Subject: [PATCH 12/33] Added disc as default list style --- lib/src/style.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/style.dart b/lib/src/style.dart index 004b32f4e7..d400eb6b0c 100644 --- a/lib/src/style.dart +++ b/lib/src/style.dart @@ -664,7 +664,7 @@ enum ListStyleType { factory ListStyleType.fromName(String name) { return ListStyleType.values.firstWhere((value) { return name == value.counterStyle; - }); + }, orElse: () => ListStyleType.disc); } } From be92255bb0e4cc75b07b132926cb8cd6c270296e Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Sun, 12 May 2024 23:10:38 +0200 Subject: [PATCH 13/33] fix: enable JavaScript in iframe When an iframe contains the `sandbox` attribute, its value must be exactly `allow-scripts` in order to enable JavaScript. However, in many cases the attribute has more than one value. Here is an example:[^1] ``` sandbox="allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox" ``` We make the condition more flexible by using `contains`. Fixes https://github.com/DocMarty84/miniflutt/issues/30 [^1]: https://github.com/miniflux/v2/blob/3388f8e376632da49be8d8785422962ba83a8518/internal/reader/sanitizer/sanitizer.go#L238 --- packages/flutter_html_iframe/lib/iframe_mobile.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_html_iframe/lib/iframe_mobile.dart b/packages/flutter_html_iframe/lib/iframe_mobile.dart index 9c73e65cba..fdb0ad13dc 100644 --- a/packages/flutter_html_iframe/lib/iframe_mobile.dart +++ b/packages/flutter_html_iframe/lib/iframe_mobile.dart @@ -20,7 +20,7 @@ class IframeWidget extends StatelessWidget { final sandboxMode = extensionContext.attributes["sandbox"]; controller.setJavaScriptMode( - sandboxMode == null || sandboxMode == "allow-scripts" + sandboxMode == null || sandboxMode.contains("allow-scripts") ? JavaScriptMode.unrestricted : JavaScriptMode.disabled); From e9b837e31dc080028b2399bcd266aa7f0b87dd39 Mon Sep 17 00:00:00 2001 From: Danil Karasev <88264176+DanilKarasev@users.noreply.github.com> Date: Thu, 16 May 2024 03:31:54 +0800 Subject: [PATCH 14/33] Update styled_element.dart --- lib/src/tree/styled_element.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/src/tree/styled_element.dart b/lib/src/tree/styled_element.dart index bf2b30f5dc..0797bfe69a 100644 --- a/lib/src/tree/styled_element.dart +++ b/lib/src/tree/styled_element.dart @@ -4,7 +4,7 @@ import 'package:flutter_html/src/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. //ignore: implementation_imports -import 'package:html/src/query_selector.dart'; +import 'package:html/src/query_selector.dart' as qs; import 'package:list_counter/list_counter.dart'; /// A [StyledElement] applies a style to all of its children. @@ -26,6 +26,14 @@ class StyledElement { required this.node, }); + bool matches(dom.Element element, String selector) { + try { + return qs.matches(element, selector); + } catch (_) { + return false; + } + } + bool matchesSelector(String selector) { return (element != null && matches(element!, selector)) || name == selector; } From 86f0d896bf7e539e88e3296ec0bf294e33d76f9a Mon Sep 17 00:00:00 2001 From: Enguerrand ARMINJON WINDOWS Date: Tue, 23 Jul 2024 18:24:20 +0200 Subject: [PATCH 15/33] feature/a-href-overridable-style --- lib/src/builtins/interactive_element_builtin.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/builtins/interactive_element_builtin.dart b/lib/src/builtins/interactive_element_builtin.dart index e8486b0701..9dfeaa5fed 100644 --- a/lib/src/builtins/interactive_element_builtin.dart +++ b/lib/src/builtins/interactive_element_builtin.dart @@ -61,7 +61,7 @@ class InteractiveElementBuiltIn extends HtmlExtension { children: childSpan.children ?.map((e) => _processInteractableChild(context, e)) .toList(), - style: childSpan.style, + style: context.styledElement?.style.generateTextStyle() ?? childSpan.style, semanticsLabel: childSpan.semanticsLabel, recognizer: TapGestureRecognizer()..onTap = onTap, ); From ecbeafaee8d6fd40bf9dc60664de6c3b61a7a7b2 Mon Sep 17 00:00:00 2001 From: Enguerrand_ARMINJON_MAC_2 Date: Thu, 30 Jan 2025 12:59:45 +0100 Subject: [PATCH 16/33] fix-html-inside-data-table --- lib/src/css_box_widget.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/src/css_box_widget.dart b/lib/src/css_box_widget.dart index 85679ea4b7..5870496e4a 100644 --- a/lib/src/css_box_widget.dart +++ b/lib/src/css_box_widget.dart @@ -475,6 +475,12 @@ class RenderCSSBox extends RenderBox ).parentSize; } + @override + double? computeDryBaseline(covariant BoxConstraints constraints, + TextBaseline baseline) { + return null; + } + _Sizes _computeSize( {required BoxConstraints constraints, required ChildLayouter layoutChild}) { From 5bcf2f90601953966923e3832db54530f67e437e Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Mon, 10 Mar 2025 23:39:22 -0600 Subject: [PATCH 17/33] chore: Update dependency versions --- .circleci/config.yml | 2 +- .github/FUNDING.yml | 2 +- .../Package.swift | 28 ++++++++ .../FlutterGeneratedPluginSwiftPackage.swift | 3 + example/lib/main.dart | 72 +++++++++---------- .../Flutter/GeneratedPluginRegistrant.swift | 8 ++- lib/flutter_html.dart | 23 +++--- lib/src/css_box_widget.dart | 9 ++- lib/src/css_parser.dart | 2 - lib/src/style.dart | 2 - lib/src/style/fontsize.dart | 2 +- lib/src/tree/replaced_element.dart | 16 ++--- lib/src/utils.dart | 6 +- .../lib/flutter_html_all.dart | 2 +- packages/flutter_html_all/pubspec.yaml | 10 ++- .../lib/flutter_html_audio.dart | 6 +- packages/flutter_html_audio/pubspec.yaml | 9 ++- .../lib/flutter_html_iframe.dart | 2 +- .../lib/iframe_mobile.dart | 4 +- .../lib/iframe_unsupported.dart | 4 +- .../flutter_html_iframe/lib/iframe_web.dart | 4 +- .../lib/shims/dart_ui.dart | 4 +- packages/flutter_html_iframe/pubspec.yaml | 9 ++- .../lib/flutter_html_math.dart | 4 +- packages/flutter_html_math/pubspec.yaml | 11 ++- .../lib/flutter_html_svg.dart | 2 +- packages/flutter_html_svg/pubspec.yaml | 13 ++-- .../flutter_html_svg/test/test_utils.dart | 4 +- .../lib/flutter_html_table.dart | 2 +- packages/flutter_html_table/pubspec.yaml | 11 ++- .../lib/flutter_html_video.dart | 8 +-- packages/flutter_html_video/pubspec.yaml | 11 ++- pubspec.yaml | 19 +++-- test/test_utils.dart | 2 +- 34 files changed, 166 insertions(+), 150 deletions(-) create mode 100644 example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift create mode 100644 example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Sources/FlutterGeneratedPluginSwiftPackage/FlutterGeneratedPluginSwiftPackage.swift diff --git a/.circleci/config.yml b/.circleci/config.yml index 718328b1f8..872b82e336 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,7 +34,7 @@ jobs: file: coverage_report/lcov.info - run: name: Run flutter analyze - command: melos run analyze + command: melos analyze --fatal-infos - run: name: Check That Flutter Code is Formatted Correctly command: dart format -o none --set-exit-if-changed . diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 6418d1dee6..6627ba6868 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,6 +1,6 @@ # These are supported funding model platforms -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +github: Sub6Resources patreon: # Replace with a single Patreon username open_collective: flutter_html ko_fi: # Replace with a single Ko-fi username diff --git a/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift b/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift new file mode 100644 index 0000000000..b3f8b299a0 --- /dev/null +++ b/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. +// +// Generated file. Do not edit. +// + +import PackageDescription + +let package = Package( + name: "FlutterGeneratedPluginSwiftPackage", + platforms: [ + .iOS("12.0") + ], + products: [ + .library(name: "FlutterGeneratedPluginSwiftPackage", type: .static, targets: ["FlutterGeneratedPluginSwiftPackage"]) + ], + dependencies: [ + .package(name: "video_player_avfoundation", path: "/Users/matthewwhitaker/.pub-cache/hosted/pub.dev/video_player_avfoundation-2.6.7/darwin/video_player_avfoundation") + ], + targets: [ + .target( + name: "FlutterGeneratedPluginSwiftPackage", + dependencies: [ + .product(name: "video-player-avfoundation", package: "video_player_avfoundation") + ] + ) + ] +) diff --git a/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Sources/FlutterGeneratedPluginSwiftPackage/FlutterGeneratedPluginSwiftPackage.swift b/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Sources/FlutterGeneratedPluginSwiftPackage/FlutterGeneratedPluginSwiftPackage.swift new file mode 100644 index 0000000000..62e7b11aa9 --- /dev/null +++ b/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Sources/FlutterGeneratedPluginSwiftPackage/FlutterGeneratedPluginSwiftPackage.swift @@ -0,0 +1,3 @@ +// +// Generated file. Do not edit. +// diff --git a/example/lib/main.dart b/example/lib/main.dart index cb9f545ad8..08142b3629 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -62,9 +62,9 @@ const htmlData = r"""

Margin Auto on Image

display:inline-block; margin: auto; (should not center):

- +

display:block margin: auto; (should center):

- +

Support for sub/sup

Solve for xn: log2(x2+n) = 93 @@ -84,26 +84,26 @@ const htmlData = r"""

Table support (With custom styling!):

- - - - - - - - - - - - - - - - - - - -
OneTwoThree
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
Rowspan
DataData
xkcd
fDatafDatafData
+ + + + + + + + + + + + + + + + + + + +

List support:

    @@ -137,20 +137,20 @@ const htmlData = r""" Linking to websites has never been easier.

    -

    Image support:

    - - - - - - - - - -
    Network pngxkcd
    Local asset png
    Local asset svg
    Data uri (with base64 support)Red dot (png) - Green dot (base64 svg) - Green dot (plain svg) -
    Custom image render
    Broken network imageBroken network image alt text
    + + + + + + + + + + + + + +

    SVG support:

    diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index 1c2c9b0787..8e40e9f075 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,8 +5,12 @@ import FlutterMacOS import Foundation -import wakelock_macos +import package_info_plus +import video_player_avfoundation +import wakelock_plus func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) + FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) + WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) } diff --git a/lib/flutter_html.dart b/lib/flutter_html.dart index d51a5f8a1f..044e5e1ec3 100644 --- a/lib/flutter_html.dart +++ b/lib/flutter_html.dart @@ -1,4 +1,4 @@ -library flutter_html; +library; import 'package:flutter/material.dart'; import 'package:flutter_html/src/html_parser.dart'; @@ -48,7 +48,7 @@ class Html extends StatefulWidget { /// **style** Pass in the style information for the Html here. /// See [its wiki page](https://github.com/Sub6Resources/flutter_html/wiki/Style) for more info. Html({ - Key? key, + super.key, GlobalKey? anchorKey, required this.data, this.onLinkTap, @@ -61,13 +61,12 @@ class Html extends StatefulWidget { this.style = const {}, }) : documentElement = null, assert(data != null), - _anchorKey = anchorKey ?? GlobalKey(), - super(key: key); + _anchorKey = anchorKey ?? GlobalKey(); Html.fromDom({ - Key? key, + super.key, GlobalKey? anchorKey, - @required dom.Document? document, + required dom.Document? document, this.onLinkTap, this.onAnchorTap, this.extensions = const [], @@ -79,13 +78,12 @@ class Html extends StatefulWidget { }) : data = null, assert(document != null), documentElement = document!.documentElement, - _anchorKey = anchorKey ?? GlobalKey(), - super(key: key); + _anchorKey = anchorKey ?? GlobalKey(); Html.fromElement({ - Key? key, + super.key, GlobalKey? anchorKey, - @required this.documentElement, + required this.documentElement, this.onLinkTap, this.onAnchorTap, this.extensions = const [], @@ -96,8 +94,7 @@ class Html extends StatefulWidget { this.style = const {}, }) : data = null, assert(documentElement != null), - _anchorKey = anchorKey ?? GlobalKey(), - super(key: key); + _anchorKey = anchorKey ?? GlobalKey(); /// A unique key for this Html widget to ensure uniqueness of anchors final GlobalKey _anchorKey; @@ -127,7 +124,7 @@ class Html extends StatefulWidget { /// A set of the only HTML tags that should be rendered by this widget. /// - /// Note that the html parser wraps your html in an and tag + /// Note that the html parser wraps your html in an `` and `` tag /// by default, so you should include those in this set if you want any /// of your html to render. final Set? onlyRenderTheseTags; diff --git a/lib/src/css_box_widget.dart b/lib/src/css_box_widget.dart index bbba023321..02b3dc8362 100644 --- a/lib/src/css_box_widget.dart +++ b/lib/src/css_box_widget.dart @@ -81,7 +81,7 @@ class CssBoxWidget extends StatelessWidget { child: top ? child : MediaQuery( - data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), + data: MediaQuery.of(context).copyWith(textScaler: TextScaler.linear(1.0)), child: child, ), ), @@ -176,8 +176,7 @@ class CssBoxWidget extends StatelessWidget { } class _CSSBoxRenderer extends MultiChildRenderObjectWidget { - _CSSBoxRenderer({ - Key? key, + const _CSSBoxRenderer({ required super.children, required this.display, required this.margins, @@ -189,7 +188,7 @@ class _CSSBoxRenderer extends MultiChildRenderObjectWidget { required this.childIsReplaced, required this.emValue, required this.shrinkWrap, - }) : super(key: key); + }); /// The Display type of the element final Display display; @@ -803,7 +802,7 @@ extension Normalize on Dimension { double _calculateEmValue(Style style, BuildContext buildContext) { return (style.fontSize?.emValue ?? 16) * - MediaQuery.textScaleFactorOf(buildContext) * + (MediaQuery.maybeTextScalerOf(buildContext)?.scale(style.fontSize?.emValue ?? 16) ?? 1.0) * MediaQuery.of(buildContext).devicePixelRatio; } diff --git a/lib/src/css_parser.dart b/lib/src/css_parser.dart index c0fcf02f8b..57169787bc 100644 --- a/lib/src/css_parser.dart +++ b/lib/src/css_parser.dart @@ -1,5 +1,3 @@ -import 'dart:ui'; - import 'package:collection/collection.dart'; import 'package:csslib/visitor.dart' as css; import 'package:csslib/parser.dart' as cssparser; diff --git a/lib/src/style.dart b/lib/src/style.dart index 47f49b3310..3d293de8b0 100644 --- a/lib/src/style.dart +++ b/lib/src/style.dart @@ -1,5 +1,3 @@ -import 'dart:ui'; - import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_html/src/css_parser.dart'; diff --git a/lib/src/style/fontsize.dart b/lib/src/style/fontsize.dart index 793124fe14..fd6bb79652 100644 --- a/lib/src/style/fontsize.dart +++ b/lib/src/style/fontsize.dart @@ -1,7 +1,7 @@ import 'length.dart'; class FontSize extends LengthOrPercent { - FontSize(double size, [Unit unit = Unit.px]) : super(size, unit); + FontSize(super.size, [super.unit]); // These values are calculated based off of the default (`medium`) // being 14px. diff --git a/lib/src/tree/replaced_element.dart b/lib/src/tree/replaced_element.dart index f2ae859ca0..69f550ba06 100644 --- a/lib/src/tree/replaced_element.dart +++ b/lib/src/tree/replaced_element.dart @@ -34,11 +34,11 @@ class TextContentElement extends ReplacedElement { String? text; TextContentElement({ - required Style style, + required super.style, required this.text, required super.node, dom.Element? element, - }) : super(name: "[text]", style: style, elementId: "[[No ID]]"); + }) : super(name: "[text]", elementId: "[[No ID]]"); @override String toString() { @@ -54,8 +54,8 @@ class LinebreakContentElement extends ReplacedElement { } class EmptyContentElement extends ReplacedElement { - EmptyContentElement({required super.node, String name = "empty"}) - : super(name: name, style: Style(), elementId: "[[No ID]]"); + EmptyContentElement({required super.node, super.name = "empty"}) + : super(style: Style(), elementId: "[[No ID]]"); } class RubyElement extends ReplacedElement { @@ -64,13 +64,11 @@ class RubyElement extends ReplacedElement { RubyElement({ required this.element, - required List children, - String name = "ruby", + required List super.children, + super.name = "ruby", required super.node, }) : super( - name: name, alignment: PlaceholderAlignment.middle, style: Style(), - elementId: element.id, - children: children); + elementId: element.id); } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 2bd9d80d8c..e21ff8a636 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -32,10 +32,10 @@ class MultipleTapGestureDetector extends InheritedWidget { final void Function()? onTap; const MultipleTapGestureDetector({ - Key? key, - required Widget child, + super.key, + required super.child, required this.onTap, - }) : super(key: key, child: child); + }); static MultipleTapGestureDetector? of(BuildContext context) { return context diff --git a/packages/flutter_html_all/lib/flutter_html_all.dart b/packages/flutter_html_all/lib/flutter_html_all.dart index 2b34957522..75834d1bc8 100644 --- a/packages/flutter_html_all/lib/flutter_html_all.dart +++ b/packages/flutter_html_all/lib/flutter_html_all.dart @@ -1,6 +1,6 @@ /// Package flutter_html_all is used to get access to all /// of the extended features of the flutter_html package. -library flutter_html_all; +library; export 'package:flutter_html_audio/flutter_html_audio.dart'; export 'package:flutter_html_iframe/flutter_html_iframe.dart'; diff --git a/packages/flutter_html_all/pubspec.yaml b/packages/flutter_html_all/pubspec.yaml index ae6a082b17..4aaea941cf 100644 --- a/packages/flutter_html_all/pubspec.yaml +++ b/packages/flutter_html_all/pubspec.yaml @@ -4,13 +4,12 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.12.0 <4.0.0" - flutter: ">=2.2.0" + sdk: ">=3.2.0 <4.0.0" + flutter: ">=3.0.0" dependencies: flutter: sdk: flutter - html: '>=0.15.0 <1.0.0' flutter_html: ^3.0.0-beta.2 flutter_html_audio: ^3.0.0-beta.2 flutter_html_iframe: ^3.0.0-beta.2 @@ -22,14 +21,13 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.1 + flutter_lints: ^5.0.0 funding: - - https://opencollective.com/flutter_html + - https://github.com/sponsors/Sub6Resources topics: - html - css - widgets - layout - - flutter_html diff --git a/packages/flutter_html_audio/lib/flutter_html_audio.dart b/packages/flutter_html_audio/lib/flutter_html_audio.dart index ecd781d337..78816f9af9 100644 --- a/packages/flutter_html_audio/lib/flutter_html_audio.dart +++ b/packages/flutter_html_audio/lib/flutter_html_audio.dart @@ -1,4 +1,4 @@ -library flutter_html_audio; +library; import 'package:chewie_audio/chewie_audio.dart'; import 'package:flutter/material.dart'; @@ -37,10 +37,10 @@ class AudioWidget extends StatefulWidget { final AudioControllerCallback? callback; const AudioWidget({ - Key? key, + super.key, required this.context, this.callback, - }) : super(key: key); + }); @override State createState() => _AudioWidgetState(); diff --git a/packages/flutter_html_audio/pubspec.yaml b/packages/flutter_html_audio/pubspec.yaml index 3c414a36f1..ccb6156176 100644 --- a/packages/flutter_html_audio/pubspec.yaml +++ b/packages/flutter_html_audio/pubspec.yaml @@ -4,8 +4,8 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.12.0 <4.0.0" - flutter: ">=2.2.0" + sdk: ">=3.2.0 <4.0.0" + flutter: ">=3.0.0" dependencies: flutter: @@ -18,14 +18,13 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.1 + flutter_lints: ^5.0.0 funding: - - https://opencollective.com/flutter_html + - https://github.com/sponsors/Sub6Resources topics: - html - css - audio - layout - - flutter_html diff --git a/packages/flutter_html_iframe/lib/flutter_html_iframe.dart b/packages/flutter_html_iframe/lib/flutter_html_iframe.dart index 18ff5dead8..cbcf4cde5b 100644 --- a/packages/flutter_html_iframe/lib/flutter_html_iframe.dart +++ b/packages/flutter_html_iframe/lib/flutter_html_iframe.dart @@ -1,4 +1,4 @@ -library flutter_html_iframe; +library; import 'package:flutter/widgets.dart'; import 'package:flutter_html/flutter_html.dart'; diff --git a/packages/flutter_html_iframe/lib/iframe_mobile.dart b/packages/flutter_html_iframe/lib/iframe_mobile.dart index cf56898f6b..e6bbb083bc 100644 --- a/packages/flutter_html_iframe/lib/iframe_mobile.dart +++ b/packages/flutter_html_iframe/lib/iframe_mobile.dart @@ -11,10 +11,10 @@ class IframeWidget extends StatelessWidget { final ExtensionContext extensionContext; const IframeWidget({ - Key? key, + super.key, required this.extensionContext, this.navigationDelegate, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/packages/flutter_html_iframe/lib/iframe_unsupported.dart b/packages/flutter_html_iframe/lib/iframe_unsupported.dart index 137b5f0859..2e12a55c74 100644 --- a/packages/flutter_html_iframe/lib/iframe_unsupported.dart +++ b/packages/flutter_html_iframe/lib/iframe_unsupported.dart @@ -4,10 +4,10 @@ import 'package:webview_flutter/webview_flutter.dart'; class IframeWidget extends StatelessWidget { const IframeWidget({ - Key? key, + super.key, required ExtensionContext? extensionContext, NavigationDelegate? navigationDelegate, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/packages/flutter_html_iframe/lib/iframe_web.dart b/packages/flutter_html_iframe/lib/iframe_web.dart index 3751c87a71..99ab6d4ad2 100644 --- a/packages/flutter_html_iframe/lib/iframe_web.dart +++ b/packages/flutter_html_iframe/lib/iframe_web.dart @@ -14,10 +14,10 @@ class IframeWidget extends StatelessWidget { final ExtensionContext extensionContext; const IframeWidget({ - Key? key, + super.key, required this.extensionContext, this.navigationDelegate, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/packages/flutter_html_iframe/lib/shims/dart_ui.dart b/packages/flutter_html_iframe/lib/shims/dart_ui.dart index bce4ad1b17..0131b7ebda 100644 --- a/packages/flutter_html_iframe/lib/shims/dart_ui.dart +++ b/packages/flutter_html_iframe/lib/shims/dart_ui.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/// This file shims dart:ui in web-only scenarios, getting rid of the need to -/// suppress analyzer warnings. +// This file shims dart:ui in web-only scenarios, getting rid of the need to +// suppress analyzer warnings. // TODO: flutter/flutter#55000 Remove this file once web-only dart:ui APIs // are exposed from a dedicated place. diff --git a/packages/flutter_html_iframe/pubspec.yaml b/packages/flutter_html_iframe/pubspec.yaml index a9828a2e9c..cbb09e5bb7 100644 --- a/packages/flutter_html_iframe/pubspec.yaml +++ b/packages/flutter_html_iframe/pubspec.yaml @@ -4,8 +4,8 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.12.0 <4.0.0" - flutter: ">=2.2.0" + sdk: ">=3.2.0 <4.0.0" + flutter: ">=3.0.0" dependencies: flutter: @@ -17,14 +17,13 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.1 + flutter_lints: ^5.0.0 funding: - - https://opencollective.com/flutter_html + - https://github.com/sponsors/Sub6Resources topics: - html - css - iframe - layout - - flutter_html diff --git a/packages/flutter_html_math/lib/flutter_html_math.dart b/packages/flutter_html_math/lib/flutter_html_math.dart index afe0e62843..280f176178 100644 --- a/packages/flutter_html_math/lib/flutter_html_math.dart +++ b/packages/flutter_html_math/lib/flutter_html_math.dart @@ -1,4 +1,4 @@ -library flutter_html_math; +library; import 'package:html/dom.dart' as dom; import 'package:flutter/material.dart'; @@ -7,7 +7,7 @@ import 'package:flutter_math_fork/flutter_math.dart'; export 'package:flutter_math_fork/flutter_math.dart'; -/// [MathHtmlExtension] adds support for the tag to the flutter_html +/// [MathHtmlExtension] adds support for the `` tag to the flutter_html /// library. class MathHtmlExtension extends HtmlExtension { final OnMathErrorBuilder? onMathErrorBuilder; diff --git a/packages/flutter_html_math/pubspec.yaml b/packages/flutter_html_math/pubspec.yaml index 375538de19..77fd15d073 100644 --- a/packages/flutter_html_math/pubspec.yaml +++ b/packages/flutter_html_math/pubspec.yaml @@ -4,27 +4,26 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.12.0 <4.0.0" - flutter: ">=2.2.0" + sdk: ">=3.2.0 <4.0.0" + flutter: ">=3.0.0" dependencies: flutter: sdk: flutter html: '>=0.15.0 <1.0.0' flutter_html: ^3.0.0-beta.2 - flutter_math_fork: '>=0.6.0 <1.0.0' + flutter_math_fork: '>=0.7.0 <1.0.0' dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.1 + flutter_lints: ^5.0.0 funding: - - https://opencollective.com/flutter_html + - https://github.com/sponsors/Sub6Resources topics: - html - latex - math - tex - - flutter_html diff --git a/packages/flutter_html_svg/lib/flutter_html_svg.dart b/packages/flutter_html_svg/lib/flutter_html_svg.dart index fe31364d10..3a50cebeb4 100644 --- a/packages/flutter_html_svg/lib/flutter_html_svg.dart +++ b/packages/flutter_html_svg/lib/flutter_html_svg.dart @@ -1,4 +1,4 @@ -library flutter_html_svg; +library; import 'dart:convert'; diff --git a/packages/flutter_html_svg/pubspec.yaml b/packages/flutter_html_svg/pubspec.yaml index 200fc08cb0..62ffb8e4a0 100644 --- a/packages/flutter_html_svg/pubspec.yaml +++ b/packages/flutter_html_svg/pubspec.yaml @@ -4,28 +4,27 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.17.0 <4.0.0" - flutter: ">=2.2.0" + sdk: ">=3.2.0 <4.0.0" + flutter: ">=3.0.0" dependencies: flutter: sdk: flutter html: '>=0.15.0 <1.0.0' flutter_html: ^3.0.0-beta.2 - flutter_svg: '>=1.0.0 <3.0.0' + flutter_svg: '>=2.0.0 <3.0.0' dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.1 - meta: ^1.8.0 + flutter_lints: ^5.0.0 + meta: ^1.15.0 funding: - - https://opencollective.com/flutter_html + - https://github.com/sponsors/Sub6Resources topics: - html - css - svg - images - - flutter_html diff --git a/packages/flutter_html_svg/test/test_utils.dart b/packages/flutter_html_svg/test/test_utils.dart index 7660238893..57ebe9e4ca 100644 --- a/packages/flutter_html_svg/test/test_utils.dart +++ b/packages/flutter_html_svg/test/test_utils.dart @@ -14,7 +14,7 @@ const svgString = ''' '''; final String svgEncoded = Uri.encodeFull(svgString); -final svgBase64 = base64Encode(utf8.encode(svgString) as Uint8List); +final svgBase64 = base64Encode(utf8.encode(svgString)); class FakeAssetBundle extends Fake implements AssetBundle { @override @@ -73,7 +73,7 @@ void testMatchAndRender( class TestApp extends StatelessWidget { final Widget body; - const TestApp(this.body, {Key? key}) : super(key: key); + const TestApp(this.body, {super.key}); @override Widget build(BuildContext context) { diff --git a/packages/flutter_html_table/lib/flutter_html_table.dart b/packages/flutter_html_table/lib/flutter_html_table.dart index 0ec6607b39..b4063d3a72 100644 --- a/packages/flutter_html_table/lib/flutter_html_table.dart +++ b/packages/flutter_html_table/lib/flutter_html_table.dart @@ -1,4 +1,4 @@ -library flutter_html_table; +library; import 'dart:math'; diff --git a/packages/flutter_html_table/pubspec.yaml b/packages/flutter_html_table/pubspec.yaml index 7a78b11386..072335c829 100644 --- a/packages/flutter_html_table/pubspec.yaml +++ b/packages/flutter_html_table/pubspec.yaml @@ -5,27 +5,26 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.17.0 <4.0.0" - flutter: ">=2.2.0" + sdk: ">=3.2.0 <4.0.0" + flutter: ">=3.0.0" dependencies: flutter: sdk: flutter html: '>=0.15.0 <1.0.0' flutter_html: ^3.0.0-beta.2 - flutter_layout_grid: '>=1.0.1 <3.0.0' + flutter_layout_grid: '>=2.0.7 <3.0.0' dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.1 + flutter_lints: ^5.0.0 funding: - - https://opencollective.com/flutter_html + - https://github.com/sponsors/Sub6Resources topics: - html - css - table - layout - - flutter_html diff --git a/packages/flutter_html_video/lib/flutter_html_video.dart b/packages/flutter_html_video/lib/flutter_html_video.dart index 3675b173a0..6aa5298c95 100644 --- a/packages/flutter_html_video/lib/flutter_html_video.dart +++ b/packages/flutter_html_video/lib/flutter_html_video.dart @@ -1,4 +1,4 @@ -library flutter_html_video; +library; import 'package:chewie/chewie.dart'; import 'package:flutter/material.dart'; @@ -41,12 +41,12 @@ class VideoWidget extends StatefulWidget { final List deviceOrientationsAfterFullScreen; const VideoWidget({ - Key? key, + super.key, required this.context, this.callback, this.deviceOrientationsOnEnterFullScreen, this.deviceOrientationsAfterFullScreen = DeviceOrientation.values, - }) : super(key: key); + }); @override State createState() => _VideoWidgetState(); @@ -84,7 +84,7 @@ class _VideoWidgetState extends State { break; default: _videoController = - VideoPlayerController.network(sourceUri.toString()); + VideoPlayerController.networkUrl(sourceUri); break; } _chewieController = ChewieController( diff --git a/packages/flutter_html_video/pubspec.yaml b/packages/flutter_html_video/pubspec.yaml index 4eb4a7a7c0..52ca04a3be 100644 --- a/packages/flutter_html_video/pubspec.yaml +++ b/packages/flutter_html_video/pubspec.yaml @@ -4,8 +4,8 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: ">=2.12.0 <4.0.0" - flutter: ">=2.2.0" + sdk: ">=3.2.0 <4.0.0" + flutter: ">=3.3.0" dependencies: flutter: @@ -13,19 +13,18 @@ dependencies: html: '>=0.15.0 <1.0.0' flutter_html: ^3.0.0-beta.2 video_player: '>=2.2.8 <3.0.0' - chewie: '>=1.1.0 <2.0.0' + chewie: '>=1.7.0 <2.0.0' dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.1 + flutter_lints: ^5.0.0 funding: - - https://opencollective.com/flutter_html + - https://github.com/sponsors/Sub6Resources topics: - html - css - video - widgets - - flutter_html diff --git a/pubspec.yaml b/pubspec.yaml index 75645554be..8df6ec4cda 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,18 +4,18 @@ version: 3.0.0-beta.2 homepage: https://github.com/Sub6Resources/flutter_html environment: - sdk: '>=2.17.0 <4.0.0' - flutter: '>=2.2.0' + sdk: '>=3.2.0 <4.0.0' + flutter: '>=3.0.0' dependencies: # Plugin for parsing html - html: ^0.15.3 + html: ^0.15.5 # Plugin for parsing css - csslib: ^0.17.2 + csslib: ^1.0.2 # Plugin for firstWhereOrNull extension on lists - collection: ^1.17.0 + collection: ^1.19.0 # plugin to manage lists and counting in a variety of styles list_counter: ^1.0.2 @@ -26,9 +26,9 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.1 - meta: ^1.8.0 - melos: ^3.0.1 + flutter_lints: ^5.0.0 + meta: ^1.15.0 + melos: ^6.3.2 screenshots: - description: 'An example of the output produced by flutter_html' @@ -43,11 +43,10 @@ screenshots: path: example/screenshots/flutter_html_readme_screenshot.png funding: - - https://opencollective.com/flutter_html + - https://github.com/sponsors/Sub6Resources topics: - html - css - widgets - layout - - flutter_html diff --git a/test/test_utils.dart b/test/test_utils.dart index e31617caf6..f2a43f4772 100644 --- a/test/test_utils.dart +++ b/test/test_utils.dart @@ -7,7 +7,7 @@ import 'package:flutter_test/flutter_test.dart'; class TestApp extends StatelessWidget { final Widget child; - const TestApp({Key? key, required this.child}) : super(key: key); + const TestApp({super.key, required this.child}); @override Widget build(BuildContext context) { From 8787648efd0ee31835e08e3cf4a2260c120b0cb3 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 11 Mar 2025 07:50:56 -0600 Subject: [PATCH 18/33] update copyright date and example dependencies --- LICENSE | 2 +- example/.metadata | 39 ++- example/android/.gitignore | 13 + .../android/app/src/debug/AndroidManifest.xml | 7 + .../com/example/example/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../app/src/main/res/values-night/styles.xml | 18 ++ .../app/src/profile/AndroidManifest.xml | 7 + example/ios/.gitignore | 34 +++ .../Package.swift | 8 +- .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + example/ios/RunnerTests/RunnerTests.swift | 12 + example/lib/main.dart | 108 +++---- example/linux/.gitignore | 1 + example/linux/CMakeLists.txt | 128 ++++++++ example/linux/flutter/CMakeLists.txt | 88 ++++++ .../flutter/generated_plugin_registrant.cc | 11 + .../flutter/generated_plugin_registrant.h | 15 + example/linux/flutter/generated_plugins.cmake | 23 ++ example/linux/runner/CMakeLists.txt | 26 ++ example/linux/runner/main.cc | 6 + example/linux/runner/my_application.cc | 130 ++++++++ example/linux/runner/my_application.h | 18 ++ .../Flutter/GeneratedPluginRegistrant.swift | 2 + example/macos/Podfile | 2 +- .../macos/Runner.xcodeproj/project.pbxproj | 31 +- .../xcshareddata/xcschemes/Runner.xcscheme | 20 +- example/macos/Runner/AppDelegate.swift | 6 +- .../macos/Runner/DebugProfile.entitlements | 2 + example/macos/RunnerTests/RunnerTests.swift | 12 + example/pubspec.yaml | 4 +- example/test/widget_test.dart | 1 + example/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes example/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes example/windows/.gitignore | 17 ++ example/windows/CMakeLists.txt | 108 +++++++ example/windows/flutter/CMakeLists.txt | 109 +++++++ .../flutter/generated_plugin_registrant.cc | 11 + .../flutter/generated_plugin_registrant.h | 15 + .../windows/flutter/generated_plugins.cmake | 23 ++ example/windows/runner/CMakeLists.txt | 40 +++ example/windows/runner/Runner.rc | 121 ++++++++ example/windows/runner/flutter_window.cpp | 71 +++++ example/windows/runner/flutter_window.h | 33 ++ example/windows/runner/main.cpp | 43 +++ example/windows/runner/resource.h | 16 + example/windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes example/windows/runner/runner.exe.manifest | 14 + example/windows/runner/utils.cpp | 65 ++++ example/windows/runner/utils.h | 19 ++ example/windows/runner/win32_window.cpp | 288 ++++++++++++++++++ example/windows/runner/win32_window.h | 102 +++++++ packages/flutter_html_all/LICENSE | 2 +- packages/flutter_html_audio/LICENSE | 2 +- packages/flutter_html_iframe/LICENSE | 2 +- packages/flutter_html_math/LICENSE | 2 +- packages/flutter_html_svg/LICENSE | 2 +- packages/flutter_html_table/LICENSE | 2 +- .../lib/flutter_html_table.dart | 2 +- packages/flutter_html_video/LICENSE | 2 +- 63 files changed, 1857 insertions(+), 77 deletions(-) create mode 100644 example/android/.gitignore create mode 100644 example/android/app/src/debug/AndroidManifest.xml create mode 100644 example/android/app/src/main/kotlin/com/example/example/MainActivity.kt create mode 100644 example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 example/android/app/src/main/res/values-night/styles.xml create mode 100644 example/android/app/src/profile/AndroidManifest.xml create mode 100644 example/ios/.gitignore create mode 100644 example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 example/ios/RunnerTests/RunnerTests.swift create mode 100644 example/linux/.gitignore create mode 100644 example/linux/CMakeLists.txt create mode 100644 example/linux/flutter/CMakeLists.txt create mode 100644 example/linux/flutter/generated_plugin_registrant.cc create mode 100644 example/linux/flutter/generated_plugin_registrant.h create mode 100644 example/linux/flutter/generated_plugins.cmake create mode 100644 example/linux/runner/CMakeLists.txt create mode 100644 example/linux/runner/main.cc create mode 100644 example/linux/runner/my_application.cc create mode 100644 example/linux/runner/my_application.h create mode 100644 example/macos/RunnerTests/RunnerTests.swift create mode 100644 example/test/widget_test.dart create mode 100644 example/web/icons/Icon-maskable-192.png create mode 100644 example/web/icons/Icon-maskable-512.png create mode 100644 example/windows/.gitignore create mode 100644 example/windows/CMakeLists.txt create mode 100644 example/windows/flutter/CMakeLists.txt create mode 100644 example/windows/flutter/generated_plugin_registrant.cc create mode 100644 example/windows/flutter/generated_plugin_registrant.h create mode 100644 example/windows/flutter/generated_plugins.cmake create mode 100644 example/windows/runner/CMakeLists.txt create mode 100644 example/windows/runner/Runner.rc create mode 100644 example/windows/runner/flutter_window.cpp create mode 100644 example/windows/runner/flutter_window.h create mode 100644 example/windows/runner/main.cpp create mode 100644 example/windows/runner/resource.h create mode 100644 example/windows/runner/resources/app_icon.ico create mode 100644 example/windows/runner/runner.exe.manifest create mode 100644 example/windows/runner/utils.cpp create mode 100644 example/windows/runner/utils.h create mode 100644 example/windows/runner/win32_window.cpp create mode 100644 example/windows/runner/win32_window.h diff --git a/LICENSE b/LICENSE index 89971b33a6..230b35b47f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2022 The flutter_html developers +Copyright (c) 2019-2025 The flutter_html developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/.metadata b/example/.metadata index e0236519dc..b02a7e4bf2 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,7 +4,42 @@ # This file should be version controlled and should not be manually edited. version: - revision: 20e59316b8b8474554b38493b8ca888794b0234a - channel: stable + revision: "17025dd88227cd9532c33fa78f5250d548d87e9a" + channel: "stable" project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + - platform: android + create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + - platform: ios + create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + - platform: linux + create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + - platform: macos + create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + - platform: web + create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + - platform: windows + create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/example/android/.gitignore b/example/android/.gitignore new file mode 100644 index 0000000000..55afd919c6 --- /dev/null +++ b/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000000..399f6981d5 --- /dev/null +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt new file mode 100644 index 0000000000..70f8f08f24 --- /dev/null +++ b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000000..f74085f3f6 --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000000..06952be745 --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000000..399f6981d5 --- /dev/null +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/ios/.gitignore b/example/ios/.gitignore new file mode 100644 index 0000000000..7a7f9873ad --- /dev/null +++ b/example/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift b/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift index b3f8b299a0..5903de88b2 100644 --- a/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift +++ b/example/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift @@ -15,13 +15,17 @@ let package = Package( .library(name: "FlutterGeneratedPluginSwiftPackage", type: .static, targets: ["FlutterGeneratedPluginSwiftPackage"]) ], dependencies: [ - .package(name: "video_player_avfoundation", path: "/Users/matthewwhitaker/.pub-cache/hosted/pub.dev/video_player_avfoundation-2.6.7/darwin/video_player_avfoundation") + .package(name: "package_info_plus", path: "/Users/matthewwhitaker/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/ios/package_info_plus"), + .package(name: "video_player_avfoundation", path: "/Users/matthewwhitaker/.pub-cache/hosted/pub.dev/video_player_avfoundation-2.7.0/darwin/video_player_avfoundation"), + .package(name: "webview_flutter_wkwebview", path: "/Users/matthewwhitaker/.pub-cache/hosted/pub.dev/webview_flutter_wkwebview-3.18.4/darwin/webview_flutter_wkwebview") ], targets: [ .target( name: "FlutterGeneratedPluginSwiftPackage", dependencies: [ - .product(name: "video-player-avfoundation", package: "video_player_avfoundation") + .product(name: "package-info-plus", package: "package_info_plus"), + .product(name: "video-player-avfoundation", package: "video_player_avfoundation"), + .product(name: "webview-flutter-wkwebview", package: "webview_flutter_wkwebview") ] ) ] diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000..f9b0d7c5ea --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000..f9b0d7c5ea --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/RunnerTests/RunnerTests.swift b/example/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000000..86a7c3b1b6 --- /dev/null +++ b/example/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 08142b3629..893908f737 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -21,7 +21,7 @@ class MyApp extends StatelessWidget { } class MyHomePage extends StatefulWidget { - const MyHomePage({Key? key, required this.title}) : super(key: key); + const MyHomePage({super.key, required this.title}); final String title; @@ -37,20 +37,20 @@ const htmlData = r"""

    Header 4

    Header 5
    Header 6
    - +

    Inline Styles:

    The should be BLUE style='color: blue;'

    The should be RED style='color: red;'

    The should be BLACK with 10% alpha style='color: rgba(0, 0, 0, 0.10);

    The should be GREEN style='color: rgb(0, 97, 0);

    The should be GREEN style='color: rgb(0, 97, 0);

    - +

    Text Alignment

    Center Aligned Text

    Right Aligned Text

    Justified Text

    Center Aligned Text

    - +

    Margins

    Default Div (width 350px height 20px)
    margin-left: 3em
    @@ -59,17 +59,17 @@ const htmlData = r"""
    margin-left: auto
    margin-right: auto
    margin-left: auto; margin-right: 3em
    - +

    Margin Auto on Image

    display:inline-block; margin: auto; (should not center):

    - +

    display:block margin: auto; (should center):

    - - + +

    Support for sub/sup

    Solve for xn: log2(x2+n) = 93

    One of the most common equations in all of physics is
    E=mc2.

    - +

    Ruby Support:

    @@ -78,33 +78,33 @@ const htmlData = r"""  is Japanese Kanji.

    - +

    Support for maxLines:

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vestibulum sapien feugiat lorem tempor, id porta orci elementum. Fusce sed justo id arcu egestas congue. Fusce tincidunt lacus ipsum, in imperdiet felis ultricies eu. In ullamcorper risus felis, ac maximus dui bibendum vel. Integer ligula tortor, facilisis eu mauris ut, ultrices hendrerit ex. Donec scelerisque massa consequat, eleifend mauris eu, mollis dui. Donec placerat augue tortor, et tincidunt quam tempus non. Quisque sagittis enim nisi, eu condimentum lacus egestas ac. Nam facilisis luctus ipsum, at aliquam urna fermentum a. Quisque tortor dui, faucibus in ante eget, pellentesque mattis nibh. In augue dolor, euismod vitae eleifend nec, tempus vel urna. Donec vitae augue accumsan ligula fringilla ultrices et vel ex.
    - - + +

    Table support (With custom styling!):

    - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + +
    OneTwoThree
    Rowspan
    Rowspan
    Rowspan
    Rowspan
    Rowspan
    Rowspan
    Rowspan
    Rowspan
    Rowspan
    Rowspan
    Rowspan
    Rowspan
    Rowspan
    Rowspan
    DataData
    xkcd
    fDatafDatafData
    +

    List support:

    1. This
    2. @@ -131,43 +131,43 @@ const htmlData = r"""
    3. Header 2

    4. Header 2
    - +

    Link support:

    Linking to websites has never been easier.

    - - - - - - - - - - - - - - - - + +

    Image support:

    + + + + + + + + + +
    Network pngxkcd
    Local asset png
    Local asset svg
    Data uri (with base64 support)Red dot (png) + Green dot (base64 svg) + Green dot (plain svg) +
    Custom image render
    Broken network imageBroken network image alt text
    +

    SVG support:

    - +

    Custom Element Support:

    Inline: <bird></bird> becomes: .
    - + Block: <flutter></flutter> becomes: and <flutter horizontal></flutter> becomes: - +

    MathML Support:

    @@ -261,7 +261,7 @@ const htmlData = r""" = 1 - +

    Tex Support with the custom tex tag:

    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)

    Scroll to top

    diff --git a/example/linux/.gitignore b/example/linux/.gitignore new file mode 100644 index 0000000000..d3896c9844 --- /dev/null +++ b/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt new file mode 100644 index 0000000000..7a9a314f92 --- /dev/null +++ b/example/linux/CMakeLists.txt @@ -0,0 +1,128 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "example") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/example/linux/flutter/CMakeLists.txt b/example/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000000..d5bd01648a --- /dev/null +++ b/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000000..e71a16d23d --- /dev/null +++ b/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/example/linux/flutter/generated_plugin_registrant.h b/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000000..e0f0a47bc0 --- /dev/null +++ b/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/example/linux/flutter/generated_plugins.cmake b/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000000..2e1de87a7e --- /dev/null +++ b/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/example/linux/runner/CMakeLists.txt b/example/linux/runner/CMakeLists.txt new file mode 100644 index 0000000000..e97dabc702 --- /dev/null +++ b/example/linux/runner/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the application ID. +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") diff --git a/example/linux/runner/main.cc b/example/linux/runner/main.cc new file mode 100644 index 0000000000..e7c5c54370 --- /dev/null +++ b/example/linux/runner/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/example/linux/runner/my_application.cc b/example/linux/runner/my_application.cc new file mode 100644 index 0000000000..6c81082380 --- /dev/null +++ b/example/linux/runner/my_application.cc @@ -0,0 +1,130 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "example"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + // Set the program name to the application ID, which helps various systems + // like GTK and desktop environments map this running application to its + // corresponding .desktop file. This ensures better integration by allowing + // the application to be recognized beyond its binary name. + g_set_prgname(APPLICATION_ID); + + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/example/linux/runner/my_application.h b/example/linux/runner/my_application.h new file mode 100644 index 0000000000..72271d5e41 --- /dev/null +++ b/example/linux/runner/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index 8e40e9f075..2671e8141a 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,9 +8,11 @@ import Foundation import package_info_plus import video_player_avfoundation import wakelock_plus +import webview_flutter_wkwebview func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) + WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin")) } diff --git a/example/macos/Podfile b/example/macos/Podfile index dade8dfad0..049abe2954 100644 --- a/example/macos/Podfile +++ b/example/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.11' +platform :osx, '10.14' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index 6f0fb6010c..389bee53bf 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -27,6 +27,7 @@ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 5F522694C62AF8897C0CC60C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 504E42C3FE1FD01681D066A3 /* Pods_Runner.framework */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -80,6 +81,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 5F522694C62AF8897C0CC60C /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -175,6 +177,9 @@ /* Begin PBXNativeTarget section */ 33CC10EC2044A3C60003C045 /* Runner */ = { + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( @@ -200,10 +205,13 @@ /* Begin PBXProject section */ 33CC10E52044A3C60003C045 /* Project object */ = { + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -256,6 +264,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -404,7 +413,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -483,7 +492,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -530,7 +539,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -627,6 +636,18 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index ae8ff59d97..6172342eec 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/example/macos/Runner/DebugProfile.entitlements b/example/macos/Runner/DebugProfile.entitlements index dddb8a30c8..c946719a1a 100644 --- a/example/macos/Runner/DebugProfile.entitlements +++ b/example/macos/Runner/DebugProfile.entitlements @@ -8,5 +8,7 @@ com.apple.security.network.server + com.apple.security.network.client + diff --git a/example/macos/RunnerTests/RunnerTests.swift b/example/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000000..61f3bd1fc5 --- /dev/null +++ b/example/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 71e0332d40..308b3a2825 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none version: 1.0.0+1 environment: - sdk: '>=2.17.0 <3.0.0' + sdk: '>=3.2.0 <4.0.0' dependencies: flutter_html: @@ -17,7 +17,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.1 + flutter_lints: ^5.0.0 flutter: diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart new file mode 100644 index 0000000000..ab73b3a234 --- /dev/null +++ b/example/test/widget_test.dart @@ -0,0 +1 @@ +void main() {} diff --git a/example/web/icons/Icon-maskable-192.png b/example/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/example/web/icons/Icon-maskable-512.png b/example/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/example/windows/.gitignore b/example/windows/.gitignore new file mode 100644 index 0000000000..d492d0d98c --- /dev/null +++ b/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/example/windows/CMakeLists.txt b/example/windows/CMakeLists.txt new file mode 100644 index 0000000000..d960948af6 --- /dev/null +++ b/example/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(example LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/example/windows/flutter/CMakeLists.txt b/example/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000000..903f4899d6 --- /dev/null +++ b/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/example/windows/flutter/generated_plugin_registrant.cc b/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000000..8b6d4680af --- /dev/null +++ b/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/example/windows/flutter/generated_plugin_registrant.h b/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000000..dc139d85a9 --- /dev/null +++ b/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/example/windows/flutter/generated_plugins.cmake b/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000000..b93c4c30c1 --- /dev/null +++ b/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/example/windows/runner/CMakeLists.txt b/example/windows/runner/CMakeLists.txt new file mode 100644 index 0000000000..394917c053 --- /dev/null +++ b/example/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/example/windows/runner/Runner.rc b/example/windows/runner/Runner.rc new file mode 100644 index 0000000000..289fc7ee69 --- /dev/null +++ b/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2025 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/example/windows/runner/flutter_window.cpp b/example/windows/runner/flutter_window.cpp new file mode 100644 index 0000000000..955ee3038f --- /dev/null +++ b/example/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/example/windows/runner/flutter_window.h b/example/windows/runner/flutter_window.h new file mode 100644 index 0000000000..6da0652f05 --- /dev/null +++ b/example/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/example/windows/runner/main.cpp b/example/windows/runner/main.cpp new file mode 100644 index 0000000000..a61bf80d31 --- /dev/null +++ b/example/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/example/windows/runner/resource.h b/example/windows/runner/resource.h new file mode 100644 index 0000000000..66a65d1e4a --- /dev/null +++ b/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/example/windows/runner/resources/app_icon.ico b/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/example/windows/runner/runner.exe.manifest b/example/windows/runner/runner.exe.manifest new file mode 100644 index 0000000000..153653e8d6 --- /dev/null +++ b/example/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/example/windows/runner/utils.cpp b/example/windows/runner/utils.cpp new file mode 100644 index 0000000000..3a0b46511a --- /dev/null +++ b/example/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + unsigned int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/example/windows/runner/utils.h b/example/windows/runner/utils.h new file mode 100644 index 0000000000..3879d54755 --- /dev/null +++ b/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/example/windows/runner/win32_window.cpp b/example/windows/runner/win32_window.cpp new file mode 100644 index 0000000000..60608d0fe5 --- /dev/null +++ b/example/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/example/windows/runner/win32_window.h b/example/windows/runner/win32_window.h new file mode 100644 index 0000000000..e901dde684 --- /dev/null +++ b/example/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/flutter_html_all/LICENSE b/packages/flutter_html_all/LICENSE index 89971b33a6..230b35b47f 100644 --- a/packages/flutter_html_all/LICENSE +++ b/packages/flutter_html_all/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2022 The flutter_html developers +Copyright (c) 2019-2025 The flutter_html developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/packages/flutter_html_audio/LICENSE b/packages/flutter_html_audio/LICENSE index 89971b33a6..230b35b47f 100644 --- a/packages/flutter_html_audio/LICENSE +++ b/packages/flutter_html_audio/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2022 The flutter_html developers +Copyright (c) 2019-2025 The flutter_html developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/packages/flutter_html_iframe/LICENSE b/packages/flutter_html_iframe/LICENSE index 89971b33a6..230b35b47f 100644 --- a/packages/flutter_html_iframe/LICENSE +++ b/packages/flutter_html_iframe/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2022 The flutter_html developers +Copyright (c) 2019-2025 The flutter_html developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/packages/flutter_html_math/LICENSE b/packages/flutter_html_math/LICENSE index 89971b33a6..230b35b47f 100644 --- a/packages/flutter_html_math/LICENSE +++ b/packages/flutter_html_math/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2022 The flutter_html developers +Copyright (c) 2019-2025 The flutter_html developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/packages/flutter_html_svg/LICENSE b/packages/flutter_html_svg/LICENSE index 89971b33a6..230b35b47f 100644 --- a/packages/flutter_html_svg/LICENSE +++ b/packages/flutter_html_svg/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2022 The flutter_html developers +Copyright (c) 2019-2025 The flutter_html developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/packages/flutter_html_table/LICENSE b/packages/flutter_html_table/LICENSE index 89971b33a6..230b35b47f 100644 --- a/packages/flutter_html_table/LICENSE +++ b/packages/flutter_html_table/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2022 The flutter_html developers +Copyright (c) 2019-2025 The flutter_html developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/packages/flutter_html_table/lib/flutter_html_table.dart b/packages/flutter_html_table/lib/flutter_html_table.dart index b4063d3a72..4ae4285bc0 100644 --- a/packages/flutter_html_table/lib/flutter_html_table.dart +++ b/packages/flutter_html_table/lib/flutter_html_table.dart @@ -200,7 +200,7 @@ Widget _layoutCells( .expand((element) => element) .toList(growable: false); } else if (child is TableSectionLayoutElement) { - rows.addAll(child.children.whereType()); + rows.addAll(child.children.whereType()); } else if (child is TableRowLayoutElement) { rows.add(child); } diff --git a/packages/flutter_html_video/LICENSE b/packages/flutter_html_video/LICENSE index 89971b33a6..230b35b47f 100644 --- a/packages/flutter_html_video/LICENSE +++ b/packages/flutter_html_video/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2022 The flutter_html developers +Copyright (c) 2019-2025 The flutter_html developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 30f2fb1f4d905e59569084bfe41c45a661f5e500 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 11 Mar 2025 10:09:56 -0600 Subject: [PATCH 19/33] Handle case where parent of non-shrink-wrapped Html has infinite width --- lib/src/css_box_widget.dart | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/src/css_box_widget.dart b/lib/src/css_box_widget.dart index 02b3dc8362..b21418986a 100644 --- a/lib/src/css_box_widget.dart +++ b/lib/src/css_box_widget.dart @@ -76,7 +76,6 @@ class CssBoxWidget extends StatelessWidget { border: style.border, color: style.backgroundColor, //Colors the padding and content boxes ), - width: _shouldExpandToFillBlock() ? double.infinity : null, padding: padding, child: top ? child @@ -155,13 +154,6 @@ class CssBoxWidget extends StatelessWidget { return null; } - /// Whether or not the content-box should expand its width to fill the - /// width available to it or if it should just let its inner content - /// determine the content-box's width. - bool _shouldExpandToFillBlock() { - return (style.display?.isBlock ?? false) && !childIsReplaced && !shrinkWrap; - } - TextDirection _checkTextDirection( BuildContext context, TextDirection? direction) { final textDirection = direction ?? Directionality.maybeOf(context); @@ -515,7 +507,7 @@ class RenderCSSBox extends RenderBox RenderBox? markerBoxChild = parentData.nextSibling; // Calculate child size - final childConstraints = constraints.copyWith( + BoxConstraints childConstraints = constraints.copyWith( maxWidth: (this.width.unit != Unit.auto) ? this.width.value : containingBlockSize.width - @@ -529,11 +521,25 @@ class RenderCSSBox extends RenderBox minWidth: (this.width.unit != Unit.auto) ? this.width.value : 0, minHeight: (this.height.unit != Unit.auto) ? this.height.value : 0, ); - final Size childSize = layoutChild(child, childConstraints); + if (markerBoxChild != null) { layoutChild(markerBoxChild, childConstraints); } + // If this element is a block element and not otherwise constrained, + // we constrain the child Container to fill the entire width of this + // Widget's parent, if possible. This is equivalent to setting + // `width: double.infinity` on the inner Container, but we do it here + // to keep the infinite width from being applied if the parent's width is + // also infinite. + if(display.isBlock && !shrinkWrap && !childIsReplaced && containingBlockSize.width.isFinite) { + childConstraints = childConstraints.enforce(BoxConstraints( + maxWidth: math.max(containingBlockSize.width, childConstraints.maxWidth), + minWidth: childConstraints.maxWidth, + )); + } + final Size childSize = layoutChild(child, childConstraints); + // Calculate used values of margins based on rules final usedMargins = _calculateUsedMargins(childSize, containingBlockSize); final horizontalMargins = @@ -550,7 +556,7 @@ class RenderCSSBox extends RenderBox width = childSize.width + horizontalMargins; height = childSize.height + verticalMargins; } else if (display.isBlock) { - width = (shrinkWrap || childIsReplaced) + width = (shrinkWrap || childIsReplaced || containingBlockSize.width.isInfinite) ? childSize.width + horizontalMargins : containingBlockSize.width; height = childSize.height + verticalMargins; From f3f36bba3b8d64c7eaa2645f0708f54433448582 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 11 Mar 2025 11:54:01 -0600 Subject: [PATCH 20/33] Switch from CircleCI to Gitub Actions --- .circleci/config.yml | 40 ------------------- .github/workflows/test.yml | 33 +++++++++++++++ example/test/widget_test.dart | 8 +++- .../builtins/interactive_element_builtin.dart | 3 +- lib/src/css_box_widget.dart | 25 ++++++++---- lib/src/tree/styled_element.dart | 2 +- .../lib/flutter_html_audio.dart | 4 +- .../lib/flutter_html_video.dart | 3 +- test/elements/a_test.dart | 5 ++- 9 files changed, 67 insertions(+), 56 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/test.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 872b82e336..0000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,40 +0,0 @@ -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: - executor: default-executor - steps: - - 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 - - run: - name: Run flutter analyze - command: melos analyze --fatal-infos - - run: - name: Check That Flutter Code is Formatted Correctly - command: dart format -o none --set-exit-if-changed . diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..0a1df1def5 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,33 @@ +name: Test flutter_html + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1 + - uses: flutter-actions/setup-flutter@54feb1e258158303e041b9eaf89314dcfbf6d38a + - name: Setup Melos + run: flutter pub global activate melos + - name: Bootstrap Project + run: flutter pub global run melos bootstrap + - name: Run Test Suite + run: flutter pub global run melos run test + - name: Compile Test Coverage Report + run: flutter pub global run melos run gen_coverage + - name: Upload Coverage to Codecov + uses: codecov/codecov-action@v5 + with: + files: coverage_report/lcov.info + disable_search: true + token: ${{ secrets.CODECOV_TOKEN }} + - name: Run Dart Analysis + run: flutter pub global run melos analyze --fatal-infos + - name: Check that `dart format` has been run on every file + run: dart format -o none --set-exit-if-changed diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index ab73b3a234..e900dfd40f 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -1 +1,7 @@ -void main() {} +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('Dummy test', () { + expect(2 + 2, equals(4)); + }); +} diff --git a/lib/src/builtins/interactive_element_builtin.dart b/lib/src/builtins/interactive_element_builtin.dart index e7a88f1098..d430b3a847 100644 --- a/lib/src/builtins/interactive_element_builtin.dart +++ b/lib/src/builtins/interactive_element_builtin.dart @@ -62,7 +62,8 @@ class InteractiveElementBuiltIn extends HtmlExtension { ?.map((e) => _processInteractableChild(context, e)) .toList(), recognizer: TapGestureRecognizer()..onTap = onTap, - style: context.styledElement?.style.generateTextStyle() ?? childSpan.style, + style: + context.styledElement?.style.generateTextStyle() ?? childSpan.style, semanticsLabel: childSpan.semanticsLabel, locale: childSpan.locale, mouseCursor: childSpan.mouseCursor, diff --git a/lib/src/css_box_widget.dart b/lib/src/css_box_widget.dart index b21418986a..c6870a3929 100644 --- a/lib/src/css_box_widget.dart +++ b/lib/src/css_box_widget.dart @@ -80,7 +80,8 @@ class CssBoxWidget extends StatelessWidget { child: top ? child : MediaQuery( - data: MediaQuery.of(context).copyWith(textScaler: TextScaler.linear(1.0)), + data: MediaQuery.of(context) + .copyWith(textScaler: TextScaler.linear(1.0)), child: child, ), ), @@ -484,8 +485,8 @@ class RenderCSSBox extends RenderBox } @override - double? computeDryBaseline(covariant BoxConstraints constraints, - TextBaseline baseline) { + double? computeDryBaseline( + covariant BoxConstraints constraints, TextBaseline baseline) { return null; } @@ -532,9 +533,15 @@ class RenderCSSBox extends RenderBox // `width: double.infinity` on the inner Container, but we do it here // to keep the infinite width from being applied if the parent's width is // also infinite. - if(display.isBlock && !shrinkWrap && !childIsReplaced && containingBlockSize.width.isFinite) { + if (display.isBlock && + !shrinkWrap && + !childIsReplaced && + containingBlockSize.width.isFinite) { childConstraints = childConstraints.enforce(BoxConstraints( - maxWidth: math.max(containingBlockSize.width, childConstraints.maxWidth), + maxWidth: math.max( + containingBlockSize.width, + childConstraints.maxWidth, + ), minWidth: childConstraints.maxWidth, )); } @@ -556,7 +563,9 @@ class RenderCSSBox extends RenderBox width = childSize.width + horizontalMargins; height = childSize.height + verticalMargins; } else if (display.isBlock) { - width = (shrinkWrap || childIsReplaced || containingBlockSize.width.isInfinite) + width = (shrinkWrap || + childIsReplaced || + containingBlockSize.width.isInfinite) ? childSize.width + horizontalMargins : containingBlockSize.width; height = childSize.height + verticalMargins; @@ -808,7 +817,9 @@ extension Normalize on Dimension { double _calculateEmValue(Style style, BuildContext buildContext) { return (style.fontSize?.emValue ?? 16) * - (MediaQuery.maybeTextScalerOf(buildContext)?.scale(style.fontSize?.emValue ?? 16) ?? 1.0) * + (MediaQuery.maybeTextScalerOf(buildContext) + ?.scale(style.fontSize?.emValue ?? 16) ?? + 1.0) * MediaQuery.of(buildContext).devicePixelRatio; } diff --git a/lib/src/tree/styled_element.dart b/lib/src/tree/styled_element.dart index 0797bfe69a..f91ebf7c8e 100644 --- a/lib/src/tree/styled_element.dart +++ b/lib/src/tree/styled_element.dart @@ -33,7 +33,7 @@ class StyledElement { return false; } } - + bool matchesSelector(String selector) { return (element != null && matches(element!, selector)) || name == selector; } diff --git a/packages/flutter_html_audio/lib/flutter_html_audio.dart b/packages/flutter_html_audio/lib/flutter_html_audio.dart index 78816f9af9..ec15cf9182 100644 --- a/packages/flutter_html_audio/lib/flutter_html_audio.dart +++ b/packages/flutter_html_audio/lib/flutter_html_audio.dart @@ -60,8 +60,8 @@ class _AudioWidgetState extends State { ]; if (sources.isNotEmpty && sources.first != null) { - audioController = VideoPlayerController.network( - sources.first ?? "", + audioController = VideoPlayerController.networkUrl( + Uri.tryParse(sources.first ?? "") ?? Uri(), ); chewieAudioController = ChewieAudioController( videoPlayerController: audioController!, diff --git a/packages/flutter_html_video/lib/flutter_html_video.dart b/packages/flutter_html_video/lib/flutter_html_video.dart index 6aa5298c95..94edcd836e 100644 --- a/packages/flutter_html_video/lib/flutter_html_video.dart +++ b/packages/flutter_html_video/lib/flutter_html_video.dart @@ -83,8 +83,7 @@ class _VideoWidgetState extends State { VideoPlayerController.file(File.fromUri(sourceUri)); break; default: - _videoController = - VideoPlayerController.networkUrl(sourceUri); + _videoController = VideoPlayerController.networkUrl(sourceUri); break; } _chewieController = ChewieController( diff --git a/test/elements/a_test.dart b/test/elements/a_test.dart index 4b67971773..a64e17135c 100644 --- a/test/elements/a_test.dart +++ b/test/elements/a_test.dart @@ -57,9 +57,10 @@ void main() { ), ), ); - expect(find.text("Hello, world!", findRichText: true), findsOneWidget); + final finder = find.textRange.ofSubstring("Hello, world!"); + expect(finder, findsOne); expect(tappedUrl, equals("")); - await tester.tap(find.text("Hello, world!", findRichText: true)); + await tester.tapOnText(finder); expect(tappedUrl, equals("https://example.com")); }); From a152527242d3380493760bd55d95603c5c8f14db Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 11 Mar 2025 11:57:05 -0600 Subject: [PATCH 21/33] Change branch name --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0a1df1def5..964fcf867d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,9 +2,9 @@ name: Test flutter_html on: pull_request: - branches: [ main ] + branches: [ master ] push: - branches: [ main ] + branches: [ master ] jobs: test: From d47a4cc5253e65c51f265365a95ae252537162e5 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 11 Mar 2025 12:00:53 -0600 Subject: [PATCH 22/33] Test melos directly --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 964fcf867d..91d27fddcd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: - name: Setup Melos run: flutter pub global activate melos - name: Bootstrap Project - run: flutter pub global run melos bootstrap + run: melos bootstrap - name: Run Test Suite run: flutter pub global run melos run test - name: Compile Test Coverage Report From 634cfdc21e6afe947cdb2f29c10f6f429883224d Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 11 Mar 2025 12:05:42 -0600 Subject: [PATCH 23/33] Adjust example dependency structure to rely on melos --- example/pubspec.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 308b3a2825..91a6cee4cc 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -7,10 +7,8 @@ environment: sdk: '>=3.2.0 <4.0.0' dependencies: - flutter_html: - path: .. - flutter_html_all: - path: ../packages/flutter_html_all + flutter_html: ^3.0.0-beta.2 + flutter_html_all: ^3.0.0-beta.2 flutter: sdk: flutter From a11a9cde49f3b0daa6eb0aa25125cea42399387a Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 11 Mar 2025 12:35:23 -0600 Subject: [PATCH 24/33] Upgrade from dart:html to pkg:web --- packages/flutter_html_iframe/lib/iframe_web.dart | 7 +++---- packages/flutter_html_iframe/pubspec.yaml | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/flutter_html_iframe/lib/iframe_web.dart b/packages/flutter_html_iframe/lib/iframe_web.dart index 99ab6d4ad2..f2f4d33805 100644 --- a/packages/flutter_html_iframe/lib/iframe_web.dart +++ b/packages/flutter_html_iframe/lib/iframe_web.dart @@ -4,8 +4,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_html_iframe/shims/dart_ui.dart' as ui; -// ignore: avoid_web_libraries_in_flutter -import 'dart:html' as html; +import 'package:web/web.dart' show HTMLIFrameElement; import 'package:webview_flutter/webview_flutter.dart'; @@ -25,10 +24,10 @@ class IframeWidget extends StatelessWidget { double.tryParse(extensionContext.attributes['width'] ?? ""); final givenHeight = double.tryParse(extensionContext.attributes['height'] ?? ""); - final html.IFrameElement iframe = html.IFrameElement() + final HTMLIFrameElement iframe = HTMLIFrameElement() ..width = (givenWidth ?? (givenHeight ?? 150) * 2).toString() ..height = (givenHeight ?? (givenWidth ?? 300) / 2).toString() - ..src = extensionContext.attributes['src'] + ..src = extensionContext.attributes['src'] ?? "" ..style.border = 'none'; final String createdViewId = _getRandString(10); ui.platformViewRegistry diff --git a/packages/flutter_html_iframe/pubspec.yaml b/packages/flutter_html_iframe/pubspec.yaml index cbb09e5bb7..772b184291 100644 --- a/packages/flutter_html_iframe/pubspec.yaml +++ b/packages/flutter_html_iframe/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: sdk: flutter html: '>=0.15.0 <1.0.0' flutter_html: ^3.0.0-beta.2 + web: '>=1.1.1 < 2.0.0' webview_flutter: '>=4.0.0 <5.0.0' dev_dependencies: From 7c6d460af41fcd49f6dac50f6cdbc1ef92fe6cc2 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 11 Mar 2025 12:38:59 -0600 Subject: [PATCH 25/33] Add missing . --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 91d27fddcd..5c85e75d34 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,4 +30,4 @@ jobs: - name: Run Dart Analysis run: flutter pub global run melos analyze --fatal-infos - name: Check that `dart format` has been run on every file - run: dart format -o none --set-exit-if-changed + run: dart format -o none --set-exit-if-changed . From ed1f5e95d906b374ecadb32916e78bf13bee07c4 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 11 Mar 2025 14:00:17 -0600 Subject: [PATCH 26/33] chore: Update README with new CI badge (#1467) --- .github/workflows/test.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5c85e75d34..3bd2e1fd11 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: Test flutter_html +name: flutter_html tests on: pull_request: diff --git a/README.md b/README.md index 7cb73ac0fe..bbcb74c0f5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # flutter_html [![pub package](https://img.shields.io/pub/v/flutter_html.svg)](https://pub.dev/packages/flutter_html) [![codecov](https://codecov.io/gh/Sub6Resources/flutter_html/branch/master/graph/badge.svg)](https://codecov.io/gh/Sub6Resources/flutter_html) -[![CircleCI](https://circleci.com/gh/Sub6Resources/flutter_html.svg?style=svg)](https://circleci.com/gh/Sub6Resources/flutter_html) +[![GitHub Actions](https://github.com/Sub6Resources/flutter_html/actions/workflows/test.yml/badge.svg)](https://github.com/Sub6Resources/flutter_html/actions) [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/Sub6Resources/flutter_html/blob/master/LICENSE) A Flutter widget for rendering HTML and CSS as Flutter widgets. From 3b72adde400ab4c23c0ab4e5352b3c33c0097295 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 11 Mar 2025 16:23:55 -0600 Subject: [PATCH 27/33] fix: fixed regression in list marker positioning (#1468) --- lib/src/css_box_widget.dart | 16 +++++++++------- lib/src/style.dart | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/src/css_box_widget.dart b/lib/src/css_box_widget.dart index c6870a3929..77d545d022 100644 --- a/lib/src/css_box_widget.dart +++ b/lib/src/css_box_widget.dart @@ -616,13 +616,15 @@ class RenderCSSBox extends RenderBox RenderBox? markerBox = childParentData.nextSibling; if (markerBox != null) { final markerBoxParentData = markerBox.parentData! as CSSBoxParentData; - final distance = (child.getDistanceToBaseline(TextBaseline.alphabetic, - onlyReal: true) ?? - 0) + - topOffset; - final offsetHeight = distance - - (markerBox.getDistanceToBaseline(TextBaseline.alphabetic) ?? - markerBox.size.height); + // final distance = (child.getDistanceToBaseline(TextBaseline.alphabetic, + // onlyReal: true) ?? + // 0) + + // topOffset; + // final offsetHeight = distance - + // (markerBox.getDistanceToBaseline(TextBaseline.alphabetic) ?? + // markerBox.size.height); + // TODO handle block children better by modifying above approach + final offsetHeight = topOffset; switch (_textDirection) { case TextDirection.rtl: markerBoxParentData.offset = Offset( diff --git a/lib/src/style.dart b/lib/src/style.dart index 3d293de8b0..e605ee43fa 100644 --- a/lib/src/style.dart +++ b/lib/src/style.dart @@ -297,7 +297,7 @@ class Style { TextStyle generateTextStyle() { return TextStyle( - backgroundColor: backgroundColor, + backgroundColor: (display?.isBlock ?? false) ? null : backgroundColor, color: color, decoration: textDecoration, decorationColor: textDecorationColor, From 8cee32105f0dbd8c700566e50f3b81bf3c4c8b0c Mon Sep 17 00:00:00 2001 From: Aswathi Nambiar <135691535+nambiarAswathi@users.noreply.github.com> Date: Wed, 12 Mar 2025 04:04:14 +0530 Subject: [PATCH 28/33] fix: Fixed Illegal RegExp in Safari in 3.0.0-beta.1 and beta.2 (#1444) --- lib/src/processing/whitespace.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/src/processing/whitespace.dart b/lib/src/processing/whitespace.dart index 6dc2e74a3f..bf5cf40277 100644 --- a/lib/src/processing/whitespace.dart +++ b/lib/src/processing/whitespace.dart @@ -226,8 +226,7 @@ class WhitespaceProcessing { /// (4) Replace any instances of two or more spaces with a single space. static String _removeUnnecessaryWhitespace(String text) { return text - .replaceAll(RegExp(r" *(?=\n)"), "") - .replaceAll(RegExp(r"(?<=\n) *"), "") + .replaceAll(RegExp(r" *\n *"), "\n") .replaceAll("\n", " ") .replaceAll("\t", " ") .replaceAll(RegExp(r" {2,}"), " "); From 0fa7e1d8fecbbbcaae4272b71082f98772e78af5 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Tue, 11 Mar 2025 16:57:25 -0600 Subject: [PATCH 29/33] chore: Release 3.0.0 --- CHANGELOG.md | 7 +++++++ example/pubspec.yaml | 4 ++-- 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 | 4 ++++ packages/flutter_html_table/pubspec.yaml | 4 ++-- packages/flutter_html_video/CHANGELOG.md | 4 ++++ packages/flutter_html_video/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- 17 files changed, 58 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 335ac38223..13a7fda044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +#### 3.0.0 *March 2025* + + - Graduate package to a stable release. See pre-releases prior to this version for changelog entries. + #### 3.0.0-beta.2 *May 2023* - Several Breaking Changes. See the [migration guide](https://github.com/Sub6Resources/flutter_html/wiki/Migration-Guides#300) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 91a6cee4cc..a619c577f2 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -7,8 +7,8 @@ environment: sdk: '>=3.2.0 <4.0.0' dependencies: - flutter_html: ^3.0.0-beta.2 - flutter_html_all: ^3.0.0-beta.2 + flutter_html: ^3.0.0 + flutter_html_all: ^3.0.0 flutter: sdk: flutter diff --git a/packages/flutter_html_all/CHANGELOG.md b/packages/flutter_html_all/CHANGELOG.md index 21728dfb58..933565d4ec 100644 --- a/packages/flutter_html_all/CHANGELOG.md +++ b/packages/flutter_html_all/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.0 + + - Graduate package to a stable release. See pre-releases prior to this version for changelog entries. + ## 3.0.0-beta.2 - Update a dependency to the latest release. diff --git a/packages/flutter_html_all/pubspec.yaml b/packages/flutter_html_all/pubspec.yaml index 4aaea941cf..7baaa65ae0 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-beta.2 +version: 3.0.0 homepage: https://github.com/Sub6Resources/flutter_html environment: @@ -10,13 +10,13 @@ environment: dependencies: flutter: sdk: flutter - flutter_html: ^3.0.0-beta.2 - flutter_html_audio: ^3.0.0-beta.2 - flutter_html_iframe: ^3.0.0-beta.2 - flutter_html_math: ^3.0.0-beta.2 - flutter_html_svg: ^3.0.0-beta.2 - flutter_html_table: ^3.0.0-beta.2 - flutter_html_video: ^3.0.0-beta.2 + flutter_html: ^3.0.0 + flutter_html_audio: ^3.0.0 + flutter_html_iframe: ^3.0.0 + flutter_html_math: ^3.0.0 + flutter_html_svg: ^3.0.0 + flutter_html_table: ^3.0.0 + flutter_html_video: ^3.0.0 dev_dependencies: flutter_test: diff --git a/packages/flutter_html_audio/CHANGELOG.md b/packages/flutter_html_audio/CHANGELOG.md index b42b09c260..3b69fe85a8 100644 --- a/packages/flutter_html_audio/CHANGELOG.md +++ b/packages/flutter_html_audio/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.0 + + - Graduate package to a stable release. See pre-releases prior to this version for changelog entries. + ## 3.0.0-beta.2 - **FIX**: improve API for ExtensionContext and export marker.dart ([#1273](https://github.com/sub6resources/flutter_html/issues/1273)). ([27e33a95](https://github.com/sub6resources/flutter_html/commit/27e33a955e872d47306db9480f74f6da2e9a028a)) diff --git a/packages/flutter_html_audio/pubspec.yaml b/packages/flutter_html_audio/pubspec.yaml index ccb6156176..18723caebc 100644 --- a/packages/flutter_html_audio/pubspec.yaml +++ b/packages/flutter_html_audio/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_html_audio description: This extension package allows the