Skip to content

Migration Guides

Matthew Whitaker edited this page May 12, 2023 · 2 revisions

3.0.0

3.0.0 modularizes the library to remove unnecessary dependencies that you may not need in your app, allowing support for all Flutter-supported platforms, and a lightweight base library that meets a lot of use cases, with the option of adding more features through 1st-party packages.

If you have any further questions after reading through this guide, feel free to make an Issue or Discussion Q&A post.

Removed / Changed Parameters

customRender

customRender has been changed to extensions, and its API is now significantly different.

extensions accepts a List<Extension>.

Migration
  1. If your current custom render returns a widget:
"flutter": (RenderContext context, Widget child) {
              return FlutterLogo(
                style: (context.tree.element!.attributes['horizontal'] != null)
                    ? FlutterLogoStyle.horizontal
                    : FlutterLogoStyle.markOnly,
                textColor: context.style.color!,
                size: context.style.fontSize!.size! * 5,
              );
            },

becomes

TagExtension(
  tagsToExtend: {"flutter"},
  builder: (extensionContext) {
    return FlutterLogo(
      style: extensionContext.attributes['horizontal'] != null
          ? FlutterLogoStyle.horizontal
          : FlutterLogoStyle.markOnly,
      textColor: extensionContext.styledElement!.style.color!,
      size: extensionContext.styledElement!.style.fontSize!.value,
    );
  },
),
  1. If your current custom render returns an InlineSpan:
"bird": (RenderContext context, Widget child) {
          return TextSpan(text: "🐦");
        },

becomes

TagExtension.inline(tagsToExtend: {"bird"}, child: TextSpan(text: "🐦")),

If you were previously using the child parameter, you'll need to override the Extension class and create your own custom extension.

For more details, see How To Use Extensions


customImageRenders

This parameter is removed and needs to be migrated to use an ImageExtension.

Migration

The old way:

ImageSourceMatcher customUriMatcher() => (attributes, element) =>
    attributes['src'] != null && attributes['src']!.startsWith("custom:");

ImageRender customImageRender() => (context, attributes, element) {
      return CustomImage.network(
        attributes["src"]!.replace("custom:", ""),
        width: double.tryParse(attributes["width"]),
        height: double.tryParse(attributes["height"]),
      );
    };

...

Html(
  customImageRenders: {
    customUriMatcher(): customImageRender(),
  }
),

becomes

Html(
  extensions: [
    ImageExtension(
      matchesAssetImages: false,
      matchesDataImages: false,
      networkSchemas: {"custom:"},
      builder: (extensionContext) {
        final element = extensionContext.styledElement as ImageElement;
        return CustomImage.network(
          element.src.replace("custom:", ""),
          width: element.width,
          height: element.height,
        );
      }
    ),
  ],
),

navigationDelegateForIframe

This parameter is removed. Add the flutter_html_iframe package, and then add this extension to the Html widget:

extensions: [
  IframeHtmlExtension(
    navigationDelegate: (request) {
      //Return decision here
    },
  ),
],

onImageError

This parameter is removed. Default behavior is just to show alt text when an image encounters an error. Please create a custom Extension if you need to do additional image error handling.


onImageTap

This parameter is removed. Instead, add the OnImageTapExtension:

Html(
  data: ...,
  extensions: [
    OnImageTapExtension(
      onImageTap: (src, imgAttributes, element) {
        // Handle an image being tapped
      },
    ),
  ],
)

onLinkTap

RenderContext has been removed from the onLinkTap callback.

onMathError

This parameter is removed. Add the flutter_html_math package, and then add this extension to the Html widget:

extensions: [
  MathHtmlExtension(
    onMathErrorBuilder: //Your error builder here
  ),
],

tagsList

This parameter has been split into two different options: doNotRenderTheseTags and onlyRenderTheseTags. All supported tags are allowed by default, including tags supported by extensions. This allows you to either restrict certain tags from rendering (say you don't want any images rendered), or allow only a certain subset of tags to render (say you only want h1-h6 and p tags to be rendered).

Both doNotRenderTheseTags and onlyRenderTheseTags take a Set<String>.

You can only apply one of these options to your Html widget. Including both will trigger an assertion.


Style Breaking Changes

If you were using the style parameter in your Html widget, there are some minor changes to the API that introduce some huge possibilites:

The Unit (changes to width, height, fontSize, margin, and lineHeight)

Version 3.0.0 introduces support for length/percent/auto units for several properties on Style.

As a basic example:

Style(
  width: 100,
)

becomes

Style(
  width: Width(100, Unit.px),
  // Including Unit.px is optional. `Unit`s generally default to px if only one argument is used in the constructor.
)

Or, if you are feeling fancy, all of the below are currently supported:

Width(1.5, Unit.em),
Width(50, Unit.percent),
Width.auto(), //Which is a shortcut for Width(0, Unit.auto), and is the default for the width parameter.

The following Style properties now have support for the Unit:

width, height, fontSize, margin, lineHeight


2.0.0

2.0.0

2.0.0 introduced support for Flutter Web and migrated the library to use null safety. There were few breaking changes. See the CHANGELOG for more information


1.0.0

1.0.0

1.0.0 is a major release with tons of new features to be excited about!

Unfortunately, to bring you these new features, there had to be a lot of breaking changes and deprecation of parameters. If you're trying to migrate your old Html widget code to 1.0.0, this is the page for you.

Breaking Changes

We completely rewrote the old parser to use a combination of RichText and Widgets so that your Html looks even more natural in your app.

useRichText now defaults to false, since the new parser has so many new features and fewer bugs than the RichTextParser. The RichTextParser has been deprecated and will be removed sometime in the next couple versions (no later than version 1.3.0).

The Easy Migration

NOTE: THIS IS THE EASY WAY OUT (i.e. you need to push a quick fix on a Friday afternoon and don't want to deal with migrating this plugin to 1.0.0)

If you would like your HTML to remain unchanged and you previously did not have useRichText set to false, then set useRichText to true to continue using the RichText parser as you were. Note that you will receive none of the benefits of 1.0.0 if you do this, so you might be better off setting your flutter_html version to 0.11.0.

Full Migration Guide

Take a look at the deprecated/changed/new parameters below, and follow the migration guide for any that you are currently using. Open an issue if you encounter any difficulties in the migration to 1.0.0. We're hoping these changes make your code much cleaner and more intuitive to you and anyone else who has to deal with your code.

Deprecated/Changed/New Parameters

data (No Changes):

data remains the same in 1.0.0 as it was before. Just pass in your HTML code and the Widget will parse it:

1.0.0:
Html(
  data: "<h1>Hello, World!</h1>",
),

css (New):

NOTE: NOT YET AVAILABLE

The css parameter is new in 1.0.0. Similar to data, but for your CSS code. Great for if you have an external css file you'd like to apply to your HTML.

1.0.0:
Html(
  data: "<h1 class='example'>Example</h1>",
  css: ".example { font-family: serif; background-color: blue; }",
),

customRender (Breaking Changes):

customRender is completely different in 1.0.0. You can also read more about customRender on its wiki page.

before 1.0.0:
Html(
  data: ...,
  customRender: (dom.Node node, children) {
    if(node is dom.Element) {
      if(node.localName == "flutter") {
        return FlutterLogo();
      }
    }
  },
),
1.0.0:
Html(
  data: ...,
  customRender: {
    "flutter": (RenderContext context, child, attributes) {
      // This example is simple and doesn't use any of the provided parameters.
      // See the linked wiki page for more examples.
      return FlutterLogo();
    }
  },
),

padding (Deprecated):

The padding parameter seems a bit silly when you can wrap the entire Html widget in a Padding widget.

Or if you still want the Html content itself to be padded, then you make the following migration:

before 1.0.0:
Html(
  data: ...,
  padding: 24,
),
1.0.0:
Html(
  data: ...,
  style: {
    "html": Style(
      padding: const EdgeInsets.all(24),
    ),
  },
),

backgroundColor (Deprecated):

The new style property makes the backgroundColor property redundant.

Here's the migration you should make:

before 1.0.0:
Html(
  data: ...,
  backgroundColor: Colors.yellow,
),
1.0.0:
Html(
  data: ...,
  style: {
    "html": Style(
      backgroundColor: Colors.yellow,
    ),
  },
),

defaultTextStyle (Deprecated):

If you were using defaultTextStyle to make the Html widget take the text style of the current theme, then you can just remove the defaultTextStyle parameter from your code. The Html widget will now automatically pull the default text style from Theme.of(context).textTheme.body1.

If you'd like your Html widget to have a different default text style, you can make the following migration:

before 1.0.0:
Html(
  data: ...,
  defaultTextStyle: myCustomTextStyle,
),
1.0.0:
Html(
  data: ...,
  style: {
    "html": Style.fromTextStyle(myCustomTextStyle),
  },
),

onLinkTap (No Changes):

The onLinkTap parameter is pretty much unchanged in version 1.0.0. However, internally, the onLinkTap callback type has been renamed from OnLinkTap to OnTap. It still has the same signature, so this shouldn't affect most people.

1.0.0:
Html(
  data: ...,
  onLinkTap: (String url) {
    //Do something (e.g. launch(url)).
  }
),

renderNewlines (Deprecated):

The new style attribute supports preserving newlines in the original code. The migration is fairly simple.

before 1.0.0:
Html(
  data: ...,
  renderNewlines: true,
),
1.0.0:
Html(
  data: ...,
  style: {
    "html": Style(whiteSpace: WhiteSpace.PRE),
  },
),

customEdgeInsets (Deprecated):

In the new parser, it's now so much easier to apply custom margins/padding to html elements. Just use the new style attribute as shown in the following example migration:

before 1.0.0:
Html(
  data: ...,
  customEdgeInsets: (dom.Node node) {
    if(node is dom.Element) {
      if(node.localName == "h1" || node.localName == "h4") {
        return EdgeInsets.all(48);
      }
    }
  },
),
1.0.0:
Html(
  data: ...,
  style: {
    "h1, h4": Style(
      margin: EdgeInsets.all(48),
    ),
  },
),

customTextStyle (Deprecated):

It's now much easier to apply custom text styles to any element using the style parameter.

before 1.0.0:
Html(
  data: ...,
  customTextStyle: (dom.Node node, TextStyle baseStyle) {
    if(node is dom.Element) {
      if(node.localName == "span" || node.localName == "h4") {
        return baseStyle.merge(TextStyle(fontFamily: 'serif'));
      }
    }
    return baseStyle;
  },
),
1.0.0:
Html(
  data: ...,
  style: {
    "span, h4": Style(
      fontFamily: 'serif',
    ),
    //Alternatively, apply a style from an existing TextStyle:
    "span, h4": Style.fromTextStyle(
      TextStyle(fontFamily: 'serif'),
    ),
  },
),

blockSpacing (Deprecated):

The new parser doesn't use a global block spacing. Now, just apply margin to the desired elements.

The following migration would be common:

before 1.0.0:
Html(
  data: ...,
  blockSpacing: 24,
),
1.0.0:
Html(
  data: ...,
  style: {
    "h1, div": Style(
      margin: EdgeInsets.symmetric(vertical: 24),
    ),
  },
),

useRichText (Deprecated):

The new parser is built off of the core concepts of the RichText parser, and brings the best of the RichText parser along with it, so you shouldn't ever need to set this parameter.

As a side note, useRichText now defaults to false and must explicitly be set to true.

customTextAlign (Deprecated):

NOTE: NOT YET AVAILABLE

Aligning text is fairly straightforward with the new style parameter.

before 1.0.0:
Html(
  data: ...,
  customTextAlign: (dom.Element elem) {
    if(elem.localName == "p") {
      return TextAlign.center;
    }
    return TextAlign.left
  },
),
1.0.0:
Html(
  data: ...,
  style: {
    //Not yet available
  },
),

onImageError (No Changes):

This parameter is unchanged.

1.0.0:
Html(
  data: ...,
  onImageError: (dynamic exception, StackTrace stackTrace) {
    //Do something when an image fails to load.
  },
),

linkStyle (Deprecated):

Just use the new style parameter to do any custom link styling.

before 1.0.0:
Html(
  data: ...,
  linkStyle: TextStyle(
    textDecoration: TextDecoration.underline,
    color: Colors.red,
  ),
),
1.0.0:
Html(
  data: ...,
  style: {
    "a": Style(
      // Note that the underline can be omitted, since the new parser merges styles with the element's 
      // default style rather than replacing that style.
      color: Colors.red,
    ),
  },
),

shrinkWrap (Changed):

Due to the way the new parser renders stuff, there may be some slight changes to the way shrink-wrapped content was rendered. For most cases, though, the result should be nearly identical to the way it was in the RichText parser.

In the past, the <body> tag was always added even if it wasn't explicitly included. Due to the new style features, the <body> tag is given a margin by default like a browser would do, so you may have to explicitly remove the margin as outlined in the migration below:

before 1.0.0:
Html(
  data: ...,
  shrinkWrap: true,
),
1.0.0:
Html(
  data: ...,
  shrinkWrap: true,
  // The <body> tag in the new parser has a default margin, so you (optionally) 
  // need to remove it to get the same result as before.
  style: {
    "body": Style(margin: EdgeInsets.zero),
  }
),

imageProperties (Deprecated):

All the different properties available in the imageProperties options are available to you either through the customRender parameter or the style parameter.

Migration guide coming soon.

onImageTap (No Changes):

There have been no changes to the onImageTap callback.

1.0.0:
Html(
  data: ...,
  onImageTap: (url) {
    //Do something with image.
  }
),

showImages (Deprecated):

We've added the more generalized parameter blacklistedElements for handling which elements are rendered or not.

before 1.0.0:
Html(
  data: ...,
  showImages: false,
),

1.0.0:

Html(
  data: ...,
  blacklistedElements: ["img"],
),

blacklistedElements (New):

This parameter is good for controlling which elements are displayed or hidden. For instance, if you didn't want <iframe> or <video> elements to be rendered, then the following could be written:

1.0.0:
Html(
  data: ...,
  blacklistedElements: ["iframe", "video"],
),

style (New):

Most of the migrations above involved this new style parameter. The style parameter is essentially a Dart object representation of a simplified subset of CSS. Styles cascade in the same way that CSS does, and this gives you fine-tuned control over every pixel of the way your HTML renders its widgets.

The style attribute takes a Map<String, Style> that maps CSS selectors to Styles.

For more info, see the style wiki page.

1.0.0:
Html(
  data: ...,
  style: {
    "body": Style(
      backgroundColor: Colors.black,
      color: Colors.white,
    ),
    "div": Style(
      border: Border(bottom: BorderSide(color: Colors.grey)),
      width: 200,
      fontFamily: 'serif',
    ),
    "a": Style(
      textDecoration: TextDecoration.none,
      backgroundColor: Colors.deepPurple,
    ),
  },
),