Render HTML content in Flutter to widgets
Content Index
Often, it's necessary to display HTML content within a Flutter application, you can use a WebView in Flutter or the scheme I present in this post.. I can give you a good example of this myself: I've been creating a Flutter application for this blog (which also includes the Academy app).
Returning to the blog, the content is inherently HTML. What you're reading right now is HTML generated by a WYSIWYG (What You See Is What You Get) JavaScript plugin. Since this content is consumed from the Flutter app via a REST API, I need to find a reliable way to present this HTML string, whether using a webview or a similar tool.
In this post we will see a plugin that allows you to display HTML content in Flutter using a plugin as there is no solution that is part of the core of Flutter.
If you are building an application in Flutter and need to render HTML content, you can use the flutter_html package. This package allows you to display HTML content inside your Flutter widgets. Below I explain how to do it:
Install:
Add flutter_html to the dependencies of your pubspec.yaml file by running the following command:
flutter pub add flutter_htmlThen get the dependencies with:
flutter pub getUse:
Import the package into your code file:
import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_html/style.dart'; // Para usar estilos CSSUse the Html widget to render HTML content:
Dart
Html( data: /* Your HTML source */, style: { /* CSS styles (not real CSS) */ 'h1': Style(color: Colors.red), 'p': Style(color: Colors.black87, fontSize: FontSize.medium), 'ul': Style(margin: EdgeInsets.symmetric(vertical: 20)), }, )In the example above, you can provide your own HTML instead of /* Your HTML source */. Styles are applied using a map where the keys are the names of the HTML tags and the values are Style objects.
Complete Example:
Here is a complete example of how to use flutter_html:
import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_html/style.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const MaterialApp( debugShowCheckedModeBanner: false, title: 'Mi App con HTML', home: HomePage(), ); } } class HomePage extends StatelessWidget { final _htmlContent = """ <div> <h1>This is a title</h1> <p>This is a <strong>paragraph</strong>.</p> <p>I like <i>dogs</i></p> <ul> <li>Elemento de lista 1</li> <li>Elemento de lista 2</li> <li>Elemento de lista 3</li> </ul> <img src='https://www.kindacode.com/wp-content/uploads/2020/11/my-dog.jpg' /> </div> """; const HomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Mi App con HTML')), body: SafeArea( child: SingleChildScrollView( child: Html( data: _htmlContent, style: { 'h1': Style(color: Colors.red), 'p': Style(color: Colors.black87, fontSize: FontSize.medium), 'ul': Style(margin: EdgeInsets.symmetric(vertical: 20)), }, ), ), ), ); } }
In this example, the HTML content is shown with custom styles. The part that allows us to customize the CSS of the rendered HTML is very interesting, with which we can show a style customized by us and not the default style in HTML.
HTML to Widgets
I want to show you a possible alternative, or rather, a simpler option if you want to display HTML content—and I emphasize HTML content—instead of using WebViews in Flutter with all the problems they caused me (I've included the video here in the next card, in case you'd like to watch it).
I found a much simpler and more direct way that will work in any type of Flutter application you want to implement. I mean, it will work whether you want to launch it for iOS, Android, Linux, the web... you get the idea, on any of the platforms Flutter supports.
Translate HTML to Widgets
Translate your HTML content into native widgets, and with that I was able to, so to speak, solve the problem that I had before. Of course, I'm making a clarification here in case it's still not very clear:
import 'package:html/parser.dart' as htmlParser;
import 'package:html/dom.dart' as html_dom;
***
List<Widget> showSectionBookByHTML(
String html,
String tokenUser,
int bookSectionId,
Function noteAdd,
BuildContext context, [
Map<String, BookSectionNoteModel> bookSectionNotesModel = const {},
double sizeText = 1.0,
]) {
List<Widget> widgets = [];
List<html_dom.Element> elementsHtml = parseHTMLContent(html);
for (var element in elementsHtml) {
print(element.localName);
switch (element.localName) {
case 'h1':
widgets.add(
Padding(
padding: const EdgeInsets.only(top: 20, bottom: 10),
child: Text(
element.text,
style: TextStyle(
fontSize: 45 * sizeText,
fontFamily: 'IBMPlexMono',
),
),
),
);
break;
case 'h2':
widgets.add(
Padding(
padding: const EdgeInsets.only(top: 20, bottom: 10),
child: SelectableText(
element.text,
style: TextStyle(
fontSize: 35 * sizeText,
fontFamily: 'IBMPlexMono',
),
),
),
);
break;
case 'h3':
widgets.add(
Padding(
padding: const EdgeInsets.only(top: 20, bottom: 10),
child: SelectableText(
element.text,
style: TextStyle(
fontSize: 30 * sizeText,
fontFamily: 'IBMPlexMono',
),
),
),
);
break;
case 'h4':
widgets.add(
Padding(
padding: const EdgeInsets.only(top: 20, bottom: 10),
child: SelectableText(
element.text,
style: TextStyle(fontSize: 25 * sizeText),
),
),
);
break;
case 'h5':
widgets.add(
Padding(
padding: const EdgeInsets.only(top: 20, bottom: 10),
child: SelectableText(
element.text,
style: TextStyle(fontSize: 20 * sizeText),
),
),
);
break;
case 'h6':
widgets.add(
Padding(
padding: const EdgeInsets.only(top: 20, bottom: 10),
child: SelectableText(
element.text,
style: TextStyle(fontSize: 18 * sizeText),
),
),
);
break;
case 'ul':
if (element.children.isNotEmpty) {
element.children.forEach((e) {
widgets.add(
_hightlightText(
"⚬ ${e.text}",
bookSectionNotesModel,
element.attributes['id'] ?? '',
"",
16 * sizeText,
tokenUser,
bookSectionId,
noteAdd,
context,
),
);
});
widgets.add(SizedBox(height: 15));
}
break;
case 'p':
// img
if (element.children.isNotEmpty &&
element.children[0].localName == 'img') {
String? src = element.children[0].attributes['src'];
if (src != null) {
widgets.add(
Padding(
padding: const EdgeInsets.only(top: 15, bottom: 15),
child: Image.network(baseUrlAcademy + src),
),
);
}
}
//*** p
// child
var idChild2 = '';
if (element.children.isNotEmpty) {
// posiblemente un span dentro del p
idChild2 = element.children[0].attributes['id'] ?? '';
}
widgets.add(
Padding(
padding: const EdgeInsets.only(bottom: 15),
child: _hightlightText(
element.text,
bookSectionNotesModel,
element.attributes['id'] ?? '',
idChild2,
16 * sizeText,
tokenUser,
bookSectionId,
noteAdd,
context,
),
),
);
break;
case 'pre':
widgets.add(
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: CodeView(code: element.text),
),
);
break;
}
}
return widgets;
}
List<html_dom.Element> _parseHTMLContent(String html) {
var document = html_parser.parse(html);
var body = document.body!.children;
} As you can see, you receive the HTML content and convert it based on the content's tags to the widget that most closely resembles it. This way, by using native widths, you can display this page on any platform supported by Flutter.
Advantages
The advantages that this scheme brings us with respect to the viewer are not only the support for any platform, but also the fact that you can now make the content interactive, adding options such as, in my case, being able to format the code, select text, and, in short, any functionality that Flutter allows on widgets, operations that you cannot implement natively in a viewer.
Next step, let's return to payment gateways, and learn how you can implement a payment gateway on Google Play with Flutter to sell services in this store.
I agree to receive announcements of interest about this Blog.
We'll see how we can display HTML content in Flutter and how to create a mechanism for displaying HTML content on ALL platforms supported by Flutter.