From 1cefca3b47aa6e6315a6f7cbccd714f5f706590f Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 2 Nov 2018 15:55:06 +0300 Subject: [PATCH 1/2] details tag support --- example/main.dart | 5 +++++ lib/html_parser.dart | 46 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/example/main.dart b/example/main.dart index cbe4b3fd71..601f43daea 100644 --- a/example/main.dart +++ b/example/main.dart @@ -116,6 +116,11 @@ class _MyHomePageState extends State {

Second header

Third header

Fourth div
+
+ Details summary +

Nested P1 of details

+

Nested P2 of details

+
""", //Optional parameters: padding: EdgeInsets.all(8.0), diff --git a/lib/html_parser.dart b/lib/html_parser.dart index b9fa8f4b19..94c1bf5bae 100644 --- a/lib/html_parser.dart +++ b/lib/html_parser.dart @@ -4,6 +4,7 @@ import 'package:html/dom.dart' as dom; typedef CustomRender = Widget Function(dom.Node node, List children); typedef OnLinkTap = void Function(String url); +typedef ParseNodeFunction = List Function(List nodeList); class HtmlParser { HtmlParser({ @@ -39,6 +40,7 @@ class HtmlParser { "data", "dd", "del", + "details", "dfn", "div", "dl", @@ -294,6 +296,8 @@ class HtmlParser { decoration: TextDecoration.lineThrough, ), ); + case "details": + return DetailsWidget(node, _parseNodeList); case "dfn": return DefaultTextStyle.merge( child: Wrap( @@ -800,3 +804,45 @@ class HtmlParser { return false; } } + +class DetailsWidget extends StatefulWidget { + final ParseNodeFunction parseFunc; + final dom.Element node; + DetailsWidget(this.node, this.parseFunc); + _DetailsWidgetState createState() => _DetailsWidgetState(); +} + +class _DetailsWidgetState extends State { + dom.Node _summaryNode; + bool _isOpen = false; + + void initState() { + super.initState(); + _summaryNode = widget.node.children + .firstWhere((el) => el.localName == "summary", orElse: () => null); + _isOpen = _summaryNode != null + ? _summaryNode.attributes.containsKey("open") + : false; + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () { + setState(() { + _isOpen = !_isOpen; + }); + }, + child: Text(_summaryNode != null ? _summaryNode.text : ""), + ), + Wrap( + children: + _isOpen ? widget.parseFunc(widget.node.nodes) : [Container()], + ) + ], + ); + } +} From 85171369ebc669c26999a91fa7029f3e623bf033 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 6 Nov 2018 13:20:30 +0300 Subject: [PATCH 2/2] details tag refactor --- .gitignore | 59 +++++++++++++++++++++++++++ example/main.dart | 9 +++-- lib/html_parser.dart | 44 +------------------- lib/widgets/details_widget.dart | 72 +++++++++++++++++++++++++++++++++ lib/widgets/widgets_lib.dart | 3 ++ 5 files changed, 141 insertions(+), 46 deletions(-) create mode 100644 lib/widgets/details_widget.dart create mode 100644 lib/widgets/widgets_lib.dart diff --git a/.gitignore b/.gitignore index 886e88e840..a740e6b54f 100644 --- a/.gitignore +++ b/.gitignore @@ -148,3 +148,62 @@ fabric.properties # End of https://www.gitignore.io/api/androidstudio +.idea/runConfigurations/main_dart.xml +.metadata +pubspec.lock +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png +android/build.gradle +android/gradle.properties +android/gradlew +android/gradlew.bat +android/settings.gradle +android/app/build.gradle +android/app/src/main/AndroidManifest.xml +android/app/src/main/java/com/example/flutterhtml/MainActivity.java +android/app/src/main/res/drawable/launch_background.xml +android/app/src/main/res/mipmap-hdpi/ic_launcher.png +android/app/src/main/res/mipmap-mdpi/ic_launcher.png +android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +android/app/src/main/res/values/styles.xml +android/gradle/wrapper/gradle-wrapper.jar +android/gradle/wrapper/gradle-wrapper.properties +ios/.gitignore +ios/Flutter/AppFrameworkInfo.plist +ios/Flutter/Debug.xcconfig +ios/Flutter/Release.xcconfig +ios/Runner/AppDelegate.h +ios/Runner/AppDelegate.m +ios/Runner/Info.plist +ios/Runner/main.m +ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png +ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png +ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png +ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png +ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png +ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +ios/Runner/Base.lproj/LaunchScreen.storyboard +ios/Runner/Base.lproj/Main.storyboard +ios/Runner.xcodeproj/project.pbxproj +ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +ios/Runner.xcworkspace/contents.xcworkspacedata +android/.gitignore +pubspec.lock +.vscode/launch.json +pubspec.lock diff --git a/example/main.dart b/example/main.dart index 601f43daea..758ecd0e17 100644 --- a/example/main.dart +++ b/example/main.dart @@ -117,9 +117,12 @@ class _MyHomePageState extends State {

Third header

Fourth div
- Details summary -

Nested P1 of details

-

Nested P2 of details

+

Details summary

+
    +
  • Details 1
  • +
  • Details 2
  • +
  • Details 3
  • +
""", //Optional parameters: diff --git a/lib/html_parser.dart b/lib/html_parser.dart index 94c1bf5bae..4d7311088a 100644 --- a/lib/html_parser.dart +++ b/lib/html_parser.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:html/parser.dart' as parser; import 'package:html/dom.dart' as dom; +import 'package:flutter_html/widgets/widgets_lib.dart'; typedef CustomRender = Widget Function(dom.Node node, List children); typedef OnLinkTap = void Function(String url); -typedef ParseNodeFunction = List Function(List nodeList); class HtmlParser { HtmlParser({ @@ -804,45 +804,3 @@ class HtmlParser { return false; } } - -class DetailsWidget extends StatefulWidget { - final ParseNodeFunction parseFunc; - final dom.Element node; - DetailsWidget(this.node, this.parseFunc); - _DetailsWidgetState createState() => _DetailsWidgetState(); -} - -class _DetailsWidgetState extends State { - dom.Node _summaryNode; - bool _isOpen = false; - - void initState() { - super.initState(); - _summaryNode = widget.node.children - .firstWhere((el) => el.localName == "summary", orElse: () => null); - _isOpen = _summaryNode != null - ? _summaryNode.attributes.containsKey("open") - : false; - } - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - GestureDetector( - onTap: () { - setState(() { - _isOpen = !_isOpen; - }); - }, - child: Text(_summaryNode != null ? _summaryNode.text : ""), - ), - Wrap( - children: - _isOpen ? widget.parseFunc(widget.node.nodes) : [Container()], - ) - ], - ); - } -} diff --git a/lib/widgets/details_widget.dart b/lib/widgets/details_widget.dart new file mode 100644 index 0000000000..2c7e556d50 --- /dev/null +++ b/lib/widgets/details_widget.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:html/dom.dart' as dom; + +typedef ParseNodeFunction = List Function(List nodeList); + +class DetailsWidget extends StatefulWidget { + final dom.Element node; + final ParseNodeFunction parseFunc; + + DetailsWidget(this.node, this.parseFunc); + + _DetailsWidgetState createState() => _DetailsWidgetState(); +} + +class _DetailsWidgetState extends State { + dom.Node _summaryNode; + bool _isOpen = false; + + void initState() { + super.initState(); + _summaryNode = widget.node.children + .firstWhere((el) => el.localName == "summary", orElse: () => null); + _isOpen = _summaryNode != null + ? _summaryNode.attributes.containsKey("open") + : false; + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildSummaryWidget(), + _buildChildrenWidget(), + ], + ); + } + + Widget _buildSummaryWidget() { + return GestureDetector( + onTap: () { + setState(() { + _isOpen = !_isOpen; + }); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Text( + _isOpen ? "\u{25BC}" : "\u{25B6}", + ), + SizedBox( + width: 6.0, + ), + Flexible( + child: Wrap( + children: _summaryNode != null + ? widget.parseFunc(_summaryNode.nodes) + : [Container()]), + ) + ], + ), + ); + } + + Widget _buildChildrenWidget() { + return Wrap( + children: _isOpen ? widget.parseFunc(widget.node.nodes) : [Container()], + ); + } +} diff --git a/lib/widgets/widgets_lib.dart b/lib/widgets/widgets_lib.dart new file mode 100644 index 0000000000..0239837fa1 --- /dev/null +++ b/lib/widgets/widgets_lib.dart @@ -0,0 +1,3 @@ +library widgets_lib; + +export 'details_widget.dart';