### Changelog Entry Example Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/docs/runbook/release.md An example of a changelog entry for a new version, listing features and fixes with references to PRs/issues and contributors. ```markdown ## 0.17.2 - Add `text-emphasis` / `text-emphasis-style` support (#1561, authored by @CaptainDario) - Fix border-radius with background-color for inline elements (#1569) ``` -------------------------------- ### Register Custom BuildOp for Rendering Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/docs/extensibility.md Implement complex functionalities by registering a `BuildOp`. This example shows how to modify the tree on parsing or customize block rendering. ```dart tree.register(BuildOp( onParsed: (tree) { // can be used to change text, inline contents, etc. return tree..append(...); }, onRenderBlock: (tree, child) { // use this to render special widget, wrap it into something else, etc. return MyCustomWidget(child: child); }, // depending on the rendering logic, you may need to adjust the execution order to "jump the line" priority: 9999, )); ``` -------------------------------- ### BuildOp: Modifying Build Tree (Before) Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/docs/migration.md This example shows the older method of modifying the build tree using detach, insertBefore, and insertAfter within the onTreeFlattening callback. ```dart final oldOp = BuildOp( onTreeFlattening: (meta, tree) { final foo = WidgetBit.inline(tree.parent!, Text('foo')); foo.insertBefore(tree); tree.detach(); } ) ``` -------------------------------- ### Implement Custom WidgetFactory with Chewie Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/packages/fwfh_chewie/README.md Extend WidgetFactory and mix in ChewieFactory to enable Chewie video playback within HtmlWidget. This setup is necessary for rendering video tags. ```dart import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:fwfh_chewie/fwfh_chewie.dart'; // ... HtmlWidget( html, factoryBuilder: () => MyWidgetFactory(), ) // ... class MyWidgetFactory extends WidgetFactory with ChewieFactory { } ``` -------------------------------- ### BuildOp: Modifying Build Tree (After) Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/docs/migration.md The new approach uses the onParsed callback to return a new tree, allowing for more declarative modifications. This example demonstrates creating a new subtree and appending a custom widget. ```dart final newOp = BuildOp( onParsed: (tree) { final newTree = tree.parent.sub(); newTree.append(WidgetBit.inline(tree, Text('foo'))); return newTree; } ) ``` -------------------------------- ### Replace Smilie Image with Emoji Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/docs/extensibility.md This example demonstrates replacing inline smilie images with corresponding emojis by creating a custom `WidgetFactory` and `BuildOp`. ```dart const kHtml = """

Hello :)!

How are you :P? """; const kSmilies = {':)': '๐Ÿ™‚'}; class SmilieScreen extends StatelessWidget { @override Widget build(BuildContext context) => Scaffold( appBar: AppBar( title: Text('SmilieScreen'), ), body: Padding( padding: const EdgeInsets.all(8.0), child: HtmlWidget( kHtml, factoryBuilder: () => _SmiliesWidgetFactory(), ), ), ); } class _SmiliesWidgetFactory extends WidgetFactory { final smilieOp = BuildOp( onParsed: (tree) { final alt = tree.element.attributes['alt']; return tree..addText(kSmilies[alt] ?? alt ?? ''); }, ); @override void parse(BuildTree tree) { final e = tree.element; if (e.localName == 'img' && e.classes.contains('smilie') && e.attributes.containsKey('alt')) { tree.register(smilieOp); return; } return super.parse(tree); } } ``` -------------------------------- ### Compose Custom WidgetFactory with Mixins Source: https://context7.com/daohoangson/flutter_widget_from_html/llms.txt Combine specific mixins to create a custom `WidgetFactory`. This example includes mixins for cached network images, URL launching, and SVG rendering. Configure mixin getters like `svgAllowDrawingOutsideViewBox` to customize behavior. Use this custom factory with `HtmlWidget` to control how HTML content is rendered. ```dart // pubspec.yaml // dependencies: // flutter_widget_from_html_core: ^0.17.2 // fwfh_cached_network_image: ^0.16.0 // fwfh_url_launcher: ^0.16.0 // fwfh_svg: ^0.16.0 import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:fwfh_cached_network_image/fwfh_cached_network_image.dart'; import 'package:fwfh_url_launcher/fwfh_url_launcher.dart'; import 'package:fwfh_svg/fwfh_svg.dart'; import 'package:flutter/material.dart'; // Combine only the mixins you need class AppWidgetFactory extends WidgetFactory with CachedNetworkImageFactory, UrlLauncherFactory, SvgFactory { // Optional: override any mixin getter to configure behavior @override bool get svgAllowDrawingOutsideViewBox => false; } class ContentWidget extends StatelessWidget { final String html; const ContentWidget({super.key, required this.html}); static final _factory = AppWidgetFactory(); @override Widget build(BuildContext context) { return HtmlWidget( html, factoryBuilder: () => _factory, renderMode: RenderMode.column, textStyle: const TextStyle(fontSize: 15, height: 1.5), customStylesBuilder: (element) { if (element.localName == 'code') { return {'background-color': '#f4f4f4', 'font-family': 'monospace'}; } return null; }, onTapUrl: (url) { // Handle internal links manually; let UrlLauncherFactory open the rest if (url.startsWith('/')) { debugPrint('Internal navigation to $url'); return true; // handled } return false; // delegate to url_launcher }, ); } } ``` -------------------------------- ### Create a Custom WidgetFactory Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/docs/extensibility.md Extend `WidgetFactory` to gain complete control over the rendering process. Override methods like `parse` to modify how the DOM is converted into Flutter widgets. This example shows a basic extension. ```dart class _MyWidgetFactory extends WidgetFactory { @override void parse(BuildTree tree) { // do something super.parse(tree); } } // somewhere in your app HtmlWidget( 'Hello World!', factoryBuilder: () => _MyWidgetFactory(), ), ``` -------------------------------- ### Implement Custom WidgetFactory with CachedNetworkImageFactory Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/packages/fwfh_cached_network_image/README.md Extend WidgetFactory and mix in CachedNetworkImageFactory to enable cached network image rendering within HtmlWidget. This setup is used when initializing HtmlWidget with a custom factory. ```dart import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:fwfh_cached_network_image/fwfh_cached_network_image.dart'; // ... HtmlWidget( html, factoryBuilder: () => MyWidgetFactory(), ) // ... class MyWidgetFactory extends WidgetFactory with CachedNetworkImageFactory {} ``` -------------------------------- ### Update Demo App Files Script Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/docs/runbook/release.md Execute this script to regenerate platform files and pubspec.lock for the demo application. The changes should then be committed. ```sh ./tool/update-demo_app-files.sh ``` -------------------------------- ### Embed Media with ChewieFactory and JustAudioFactory Source: https://context7.com/daohoangson/flutter_widget_from_html/llms.txt Combine `ChewieFactory` for `

Heading

A paragraph with strong, emphasized and colored text.

''', // all other parameters are optional, a few notable params: // specify custom styling for an element // see supported inline styling below customStylesBuilder: (element) { if (element.classes.contains('foo')) { return {'color': 'red'}; } return null; }, customWidgetBuilder: (element) { if (element.attributes['foo'] == 'bar') { // render a custom block widget that takes the full width return FooBarWidget(); } if (element.attributes['fizz'] == 'buzz') { // render a custom widget inline with surrounding text return InlineCustomWidget( child: FizzBuzzWidget(), ); } return null; }, // this callback will be triggered when user taps a link // return true to indicate the tap has been handled onTapUrl: (url) { debugPrint('tapped $url'); return true; }, // this callback will be triggered when user taps an image onTapImage: (image) { debugPrint('image tapped: \'${image.sources.first.url}\''); }, // select the render mode for HTML body // by default, a simple `Column` is rendered // consider using `ListView` or `SliverList` for better performance renderMode: RenderMode.column, // set the default styling for text textStyle: TextStyle(fontSize: 14), ) ``` -------------------------------- ### Change Text Color with customStylesBuilder Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/docs/extensibility.md Use `customStylesBuilder` to apply inline styles to DOM elements based on their classes, tags, or attributes. This example changes the color of text within a span with the class 'name' to red. ```dart HtmlWidget( 'Hello World!', customStylesBuilder: (element) { if (element.classes.contains('name')) { return {'color': 'red'}; } return null; }, ), ``` -------------------------------- ### Add Dependencies to pubspec.yaml Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/packages/fwfh_just_audio/README.md Include the necessary packages in your pubspec.yaml file to enable audio tag support. ```yaml dependencies: flutter_widget_from_html_core: any fwfh_just_audio: ^0.17.0 ``` -------------------------------- ### Import flutter_widget_from_html Package Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/packages/enhanced/README.md Import the necessary package to use the HtmlWidget. ```dart import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; ``` -------------------------------- ### Use HtmlWidget with a custom WidgetFactory Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/packages/fwfh_webview/README.md Instantiate HtmlWidget and provide a custom WidgetFactory that extends WebViewFactory to enable WebView rendering. ```dart import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:fwfh_webview/fwfh_webview.dart'; // ... HtmlWidget( html, factoryBuilder: () => MyWidgetFactory(), ) // ... class MyWidgetFactory extends WidgetFactory with WebViewFactory { // optional: override getter to configure how WebViews are built bool get webViewMediaPlaybackAlwaysAllow => true; String? get webViewUserAgent => 'My app'; } ``` -------------------------------- ### Handle Links with UrlLauncherFactory Source: https://context7.com/daohoangson/flutter_widget_from_html/llms.txt Use `UrlLauncherFactory` to open links from `` tags using the `url_launcher` plugin. This mixin automatically handles taps when `onTapUrl` is not explicitly set. ```dart // pubspec.yaml // dependencies: // flutter_widget_from_html_core: any // fwfh_url_launcher: ^0.16.0 import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:fwfh_url_launcher/fwfh_url_launcher.dart'; class MyWidgetFactory extends WidgetFactory with UrlLauncherFactory {} HtmlWidget( '

Visit flutter.dev or ' 'email us.

', factoryBuilder: () => MyWidgetFactory(), // onTapUrl not set โ†’ UrlLauncherFactory handles all taps automatically ); ``` -------------------------------- ### BuildOp.inline for Custom Inline Badges Source: https://context7.com/daohoangson/flutter_widget_from_html/llms.txt Use `BuildOp.inline` to create a custom factory for rendering `` elements as styled inline widgets. This approach is suitable for elements that should appear within text flow, like status indicators. ```dart import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:flutter/material.dart'; class BadgeFactory extends WidgetFactory { @override void parse(BuildTree tree) { if (tree.element.localName == 'badge') { final label = tree.element.text.trim(); tree.register( BuildOp.inline( alignment: PlaceholderAlignment.middle, debugLabel: 'badge', onRenderInlineBlock: (tree, child) { return Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: Colors.green, borderRadius: BorderRadius.circular(4), ), child: Text( label, style: const TextStyle(color: Colors.white, fontSize: 11), ), ); }, ), ); return; } super.parse(tree); } } // HTML: "Status: Active โ€” all systems go." ``` -------------------------------- ### Basic HtmlWidget Usage Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/packages/core/README.md Use the HtmlWidget to render basic HTML content. All other parameters are optional. ```dart HtmlWidget( // the first parameter (`html`) is required '''

Heading

A paragraph with strong, emphasized and colored text.

''', // all other parameters are optional, a few notable params: // specify custom styling for an element // see supported inline styling below customStylesBuilder: (element) { if (element.classes.contains('foo')) { return {'color': 'red'}; } return null; }, customWidgetBuilder: (element) { if (element.attributes['foo'] == 'bar') { // render a custom widget that takes the full width return FooBarWidget(); } if (element.attributes['fizz'] == 'buzz') { // render a custom widget that inlines with surrounding text return InlineCustomWidget( child: FizzBuzzWidget(), ); } return null; }, // this callback will be triggered when user taps a link // return true to indicate the tap has been handled onTapUrl: (url) { debugPrint('tapped $url'); return true; }, // this callback will be triggered when user taps an image onTapImage: (image) { debugPrint('image tapped: \'${image.sources.first.url}\''); }, // select the render mode for HTML body // by default, a simple `Column` is rendered // consider using `ListView` or `SliverList` for better performance renderMode: RenderMode.column, // set the default styling for text textStyle: TextStyle(fontSize: 14), ) ``` -------------------------------- ### Add flutter_widget_from_html Dependency Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/packages/enhanced/README.md Add this to your app's pubspec.yaml file to include the package. ```yaml dependencies: flutter_widget_from_html: ^0.17.2 ``` -------------------------------- ### Import flutter_widget_from_html_core package Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/packages/core/README.md Import the package in your Dart file before using the HtmlWidget. ```dart import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; ``` -------------------------------- ### Implement Custom WidgetFactory with UrlLauncherFactory Source: https://github.com/daohoangson/flutter_widget_from_html/blob/master/packages/fwfh_url_launcher/README.md Extend HtmlWidget to use a custom WidgetFactory that mixes in UrlLauncherFactory to handle URL launching. ```dart import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:fwfh_url_launcher/fwfh_url_launcher.dart'; // ... HtmlWidget( html, factoryBuilder: () => MyWidgetFactory(), ) // ... class MyWidgetFactory extends WidgetFactory with UrlLauncherFactory {} ``` -------------------------------- ### Custom WidgetFactory for Smilies and Aside Source: https://context7.com/daohoangson/flutter_widget_from_html/llms.txt Create a custom WidgetFactory to replace smilie images with emoji text and wrap `