Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for customRender in the RichText parser #64

Closed
Sub6Resources opened this issue Feb 8, 2019 · 22 comments · Fixed by #122
Closed

Add support for customRender in the RichText parser #64

Sub6Resources opened this issue Feb 8, 2019 · 22 comments · Fixed by #122
Assignees
Labels
enhancement New feature or request high-priority
Milestone

Comments

@Sub6Resources
Copy link
Owner

The RichText version of the parser needs some sort of extension mechanism. See #37 (comment).

@jeffmikels
Copy link
Contributor

jeffmikels commented Feb 8, 2019

Perhaps we can solve this by merging the two parsers into one that implements a virtual DOM that is more Flutter friendly and then specify how to render that DOM as widgets. Both parsers currently create actual Flutter widgets as they walk the html tree, but if we create a middle ground, Virtual DOM, we might end up with something better than both.

How to do this? Some initial ideas:

  1. Create a "Virtual DOM" that makes sense for Flutter. The one parser would walk the HTML tree and convert it into a simplified tree of Dart objects that make sense for Flutter apps. We would support a subset of the HTML spec and preserve HTML attributes in objects like Boxes, Paragraphs, Spans, Links, Images, and Tables.
  2. Create a default DOM renderer that translates each object to a real Flutter Widget. Boxes become Containers, Paragraphs become RichText, Spans and plain text become TextSpans, etc.
  3. Facilitate custom renderers in one or more of the following ways: (1) do nothing and require the end user to extend the HTML widget and override the build method (always an option anyway), (2) on HTML object creation allow the user to specify individual renderers for any specific renderer overrides desired, or (3) on HTML object creation, expose a "styles" object that will work in a fashion like CSS allowing Container styles and TextSpan styles to be associated with class and id values.

@skipness
Copy link

@jeffmikels
Do you think this react native library works like what you mentioned above? If yes, I think we can take a look how to implement all this functions in Dart way.

@Sub6Resources Sub6Resources pinned this issue Mar 12, 2019
@Sub6Resources
Copy link
Owner Author

@jeffmikels I really like the idea of adding an intermediary step to the parser (or as you said, a virtual DOM). As for implementing custom renderers, I think option 2 you suggested above is the best way to go. Like you said, the first option has always been there, but there are few who have used it and lots who want to expand the functionality of this plugin. Option 3 isn't a bad option, but it doesn't allow for additional, currently unsupported elements (e.g. <video>), to be added if desired. I think in the end we'll end up doing both options 2 and 3.

I've been thinking a lot about the general types of elements and nodes HTML has so we can create a simplified tree, and I think I have a pretty good idea of what needs to happen. I'll be working on creating a new version of the parser based on what I've been thinking that will hopefully combine the best of both parsers. 😃 When I finish with my implementation I'll open a pull request so that we can get some discussion and hopefully fix any errors before merging to master. My hope is that the new parser can coincide with a 1.0.0 release of this plugin. 🤞

@Sub6Resources Sub6Resources modified the milestones: 0.10.0, 1.0.0 Mar 12, 2019
@hiroshihorie
Copy link

Please support this asap, since there is no way to set colors. I need a custom tag that can specify partial colors.

@Sub6Resources
Copy link
Owner Author

I've created a development roadmap to show progress and planned features for version 1.0.0.

@saitbnzl
Copy link

saitbnzl commented Aug 31, 2019

See my fork if you need an urgent solution. I've added customRender to RichTextParser.

Usage: You use customRender as it is, no difference. And you need to provide customRenderTags:

Html(
...
customRenderTags: ["div", "iframe"],
customRender: (node, children)=>YourCustomWidget()
)

Edit: Don't rely on this. It was just an experiment. I've seen unexpected results. Clearly this is not compatible with the logic of rich text.

@noobiek
Copy link

noobiek commented Sep 3, 2019

Hi!
Any progress on v1.0.0?

@Sub6Resources
Copy link
Owner Author

@noobiek The first round of features is nearly there so there should be a prerelease version in the next couple weeks for people to try out! I'm currently balancing school, work, and this and several other projects, so I can't dedicate nearly as much time to this project as I want to. I'm also trying to balance bug-fixes for the current parser (the RichText parser) with implementing new features, so that's slowing things down a bit too.

@Sub6Resources Sub6Resources mentioned this issue Oct 2, 2019
20 tasks
@shamnex
Copy link

shamnex commented Oct 7, 2019

Hey man! Nice work. I'm currently working from your latest pull request and I'm trying to figure out how the new Custom Renderer works. Any pointers?

@shamnex
Copy link

shamnex commented Oct 7, 2019

After digging in a bit, I was able to figure it out but I'm still unable to get access to the node element.
I only have access to its attributes at the comment but not the HTML itself

Any pointers? This is really important and urgent

@Sub6Resources
Copy link
Owner Author

Sub6Resources commented Oct 7, 2019

@shamnex I'm still working to refine the new customRender. The only documentation I have on it at the moment is at https://github.com/Sub6Resources/flutter_html/wiki/1.0.0-Migration-Guide#customrender

If you give me a bit more info about the specifics of what you're trying to achieve with customRender, I can provide more pointers.

@shamnex
Copy link

shamnex commented Oct 9, 2019

Nice! The docs did help. But is it possible to have access to the dom node in the customRenderer?
I'm working on a project where I need to extract URL from an embeded tweet and this is only possible if I can access to the whole HTML.
An embedded tweet looks like this.

<blockquote class="twitter-tweet">
  <p lang="en" dir="ltr">Retweet in 3 seconds for goodluck<br>Just Believe!! <a 
  href="https://t.co/POgxHWzRfT">https://t.co/POgxHWzRfT</a></p>&mdash; 🦁Bold Man (@repliesbro) 
  <a href="https://twitter.com/repliesbro/status/1181471193301430273?ref_src=twsrc%5Etfw">October 8, 2019</a></blockquote> 

I'm looking out for the blocquote element as well as the class twitter-tweet and as you can see, the URL isn't on the blockquote attribute but nested in an achore tag within the blocquote. I can easily extract the URL if I have access to the childNodes of the blockquote node.

What do you think about exposing the dom Node in the customRenderer?. I went through your code and noticed it was a private field.

Thanks alot for all the hard work man.

@shamnex
Copy link

shamnex commented Oct 9, 2019

I also noticed there's an issue with Padding or Margin using the customRenderer. I can't seem to be able to make it take up the full width of the device.

@Sub6Resources
Copy link
Owner Author

Yeah, exposing the dom Node is definitely possible, I didn't think that many people would need it for custom rendering, but your use case proves otherwise.

As for the issue getting it to take up the full width, do you mind sharing either your code and/or some screenshots so I can debug that margin/padding issue? You can email it at sub6resources@gmail.com if you'd rather not share it here.

Thank you for your feedback. It has been extremely valuable for me getting customRender ready for a production-ready release.

@shamnex
Copy link

shamnex commented Oct 9, 2019

Hello man, well done once again for all your work and your quick reply.

I've attached a screenshot but if it's not enough, let me know if I can quickly set up a demo flutter project that reproduces the issue.

Here is the first image where I just return a Text('Hello') widget. You can see there's padding/margin around the widget.

1

Here's another when I try to render a custom Widget ( in this case a WebView).

2

It happened when I try to use the customRenderer to replace the blocquote.

I also noticed another bug that throws an error. Whenever you try to customRender an `iframe, it throws

The method 'map' was called on null.

Thanks for all the hardwork man

@Sub6Resources
Copy link
Owner Author

Thanks for sharing the screenshots. Its not entirely clear what the issue is from the screenshots alone, so if you could send over a demo project reproducing the issue, that would be great. Thank you!

@shamnex
Copy link

shamnex commented Oct 10, 2019

Hey man, thanks once again. I initially set up a minor project but after going through it one more time I figured out the problem.

I noticed you added padding and/or margin to the blockquote Style so All I had to do was override them.

Html(
          style: {

            'blockquote': Style(
              fontSize: 20,
              width: MediaQuery.of(context).size.width,
              **margin: EdgeInsets.zero,
              padding: EdgeInsets.zero,**
            ),
          },)

Thanks for your time.

@Sub6Resources
Copy link
Owner Author

Okay, thank you. I'll be sure to make sure this is more clear when I write the documentation! Thanks!

@shamnex
Copy link

shamnex commented Oct 10, 2019

Thanks alot man. Have you also looked at this issue I pointed out?

          customRender: {
            'iframe': (
              RenderContext renderContext,
              Widget widget,
              children,
            ) {
              return Text(
                'iFRAME',
                style: TextStyle(
                    fontSize: 30,
                    color: Colors.red,
                    fontWeight: FontWeight.w900),
              );
            },
          },
        ),

This throws an error: The getter 'attributes' was called on null.

It only happens when you try to customRender an iframe.

@Sub6Resources
Copy link
Owner Author

Yeah, I'll look into that. I think I have a pretty good idea of what the issue is and it should be a simple fix.

Were you able to figure out the issue with getting customRender to take the full width of the Html widget?

@shamnex
Copy link

shamnex commented Oct 10, 2019

Yup! as stated in my previous post.

Hey man, thanks once again. I initially set up a minor project but after going through it one more time I figured out the problem.

I noticed you added padding and/or margin to the blockquote Style so All I had to do was override them.

Html(
          style: {

            'blockquote': Style(
              fontSize: 20,
              width: MediaQuery.of(context).size.width,
              **margin: EdgeInsets.zero,
              padding: EdgeInsets.zero,**
            ),
          },)

Thanks for your time.

I was trying to use the customRender to override blockquote elements. All I had to do was override the padding and margin of blockquote elements in the style property of the HTML widget.

@Sub6Resources
Copy link
Owner Author

Awesome! Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request high-priority
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants