fleather-editor / fleather

Soft and gentle rich text editing for Flutter applications.
https://fleather-editor.github.io
Other
192 stars 34 forks source link

Update embed builder API #18

Open Amir-P opened 2 years ago

Amir-P commented 2 years ago

There are a few issues with the current embed builder API:

  1. It's verbose.
  2. You need to rewrite builder for the built-in embeds (We have only one built-in embed right now but it can change in future) if you want to provide your own builder.

Suppose that I have an embed type of icon. My embedBuilder would be this if I want to support built-in embeds (currently we only have horizontal rule) too.

embedBuilder: (context, node) {
  if (node.value.type == 'hr') {
    final theme = FleatherTheme.of(context);
    return Divider(
      height: theme.paragraph.style.fontSize *
          theme.paragraph.style.height,
      thickness: 2,
      color: Colors.grey.shade200,
    );
  }
  if (node.value.type == 'icon') {
    final data = node.value.data;
    return Icon(
      IconData(int.parse(data['codePoint']),
          fontFamily: data['fontFamily']),
      color: Colors.red,
    );
  }
  throw UnimplementedError();
},

Proposal: Instead of Widget Function(BuildContext context, EmbedNode node) as embed builder, we can have a Map<String, Widget Function(BuildContext context, EmbedNode node)> where the key is EmbedNode.EmbeddableObject.type. This way we can easily merge provided Map with a default internal Map inside editor and there is no need to rewrite built-in embed builders. It's more flexible than the current API.

Previous example can be written to this:

embedBuilder: {
  'icon': (context, node) {
    final data = node.value.data;
    return Icon(
      IconData(int.parse(data['codePoint']),
          fontFamily: data['fontFamily']),
      color: Colors.red,
    );
  }
},

Cons: Using current API we are able to return a default widget in case embed is not supported. With this change we can have a default embed builder too, which will be called if the Map does not contain EmbedNode.EmbeddableObject.type.

fallbackEmbedBuilder: (context, node) {
    return Text('Not Supported');
},
zaynetro commented 1 year ago

There is defaultFleatherEmbedBuilder that can be used as a fallback.

  Widget _embedBuilder(BuildContext context, EmbedNode node) {
    if (node.value.type == 'icon') {
      final data = node.value.data;
      return Icon(
        IconData(int.parse(data['codePoint']), fontFamily: data['fontFamily']),
        color: Color(int.parse(data['color'])),
        size: 18,
      );
    }

    defaultFleatherEmbedBuilder(context, node);
  }

IMO current API already works really well and supports the fallback scenario that you are optimizing. One possible addition to defaultFleatherEmbedBuilder could be to return Widget? if it encounters an unknown type.