diff --git a/README.md b/README.md index 285141a3..d31e9a58 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ After that, we need to map this "notes" type into a widget. In that case, I used Don't forget to add this method to the `QuillEditor` after that! ```dart -class NotesEmbedBuilder implements EmbedBuilder { +class NotesEmbedBuilder extends EmbedBuilder { NotesEmbedBuilder({required this.addEditNote}); Future Function(BuildContext context, {Document? document}) addEditNote; @@ -255,6 +255,7 @@ class NotesEmbedBuilder implements EmbedBuilder { QuillController controller, Embed node, bool readOnly, + bool inline, ) { final notes = NotesBlockEmbed(node.value.data).document; diff --git a/doc_cn.md b/doc_cn.md index d76f4c2e..e4ba57af 100644 --- a/doc_cn.md +++ b/doc_cn.md @@ -246,7 +246,7 @@ class NotesBlockEmbed extends CustomBlockEmbed { 在这里我们使用 `ListTile` 来渲染它,并使用 `onTap` 方法来编辑内容,最后不要忘记将此方法添加到 `QuillEditor` 中 ```dart -class NotesEmbedBuilder implements EmbedBuilder { +class NotesEmbedBuilder extends EmbedBuilder { NotesEmbedBuilder({required this.addEditNote}); Future Function(BuildContext context, {Document? document}) addEditNote; @@ -260,6 +260,7 @@ class NotesEmbedBuilder implements EmbedBuilder { QuillController controller, Embed node, bool readOnly, + bool inline, ) { final notes = NotesBlockEmbed(node.value.data).document; diff --git a/example/lib/pages/home_page.dart b/example/lib/pages/home_page.dart index 1aa7566a..40449aeb 100644 --- a/example/lib/pages/home_page.dart +++ b/example/lib/pages/home_page.dart @@ -487,7 +487,7 @@ class _HomePageState extends State { } } -class NotesEmbedBuilder implements EmbedBuilder { +class NotesEmbedBuilder extends EmbedBuilder { NotesEmbedBuilder({required this.addEditNote}); Future Function(BuildContext context, {Document? document}) addEditNote; @@ -501,6 +501,7 @@ class NotesEmbedBuilder implements EmbedBuilder { QuillController controller, Embed node, bool readOnly, + bool inline, ) { final notes = NotesBlockEmbed(node.value.data).document; diff --git a/example/lib/universal_ui/universal_ui.dart b/example/lib/universal_ui/universal_ui.dart index d18e7fbd..7b35f1f1 100644 --- a/example/lib/universal_ui/universal_ui.dart +++ b/example/lib/universal_ui/universal_ui.dart @@ -27,7 +27,7 @@ class UniversalUI { var ui = UniversalUI(); -class ImageEmbedBuilderWeb implements EmbedBuilder { +class ImageEmbedBuilderWeb extends EmbedBuilder { @override String get key => BlockEmbed.imageType; @@ -37,6 +37,7 @@ class ImageEmbedBuilderWeb implements EmbedBuilder { QuillController controller, Embed node, bool readOnly, + bool inline, ) { final imageUrl = node.value.data; if (isImageBase64(imageUrl)) { @@ -68,13 +69,18 @@ class ImageEmbedBuilderWeb implements EmbedBuilder { } } -class VideoEmbedBuilderWeb implements EmbedBuilder { +class VideoEmbedBuilderWeb extends EmbedBuilder { @override String get key => BlockEmbed.videoType; @override - Widget build(BuildContext context, QuillController controller, Embed node, - bool readOnly) { + Widget build( + BuildContext context, + QuillController controller, + Embed node, + bool readOnly, + bool inline, + ) { var videoUrl = node.value.data; if (videoUrl.contains('youtube.com') || videoUrl.contains('youtu.be')) { final youtubeID = YoutubePlayer.convertUrlToId(videoUrl); diff --git a/flutter_quill_extensions/lib/embeds/builders.dart b/flutter_quill_extensions/lib/embeds/builders.dart index 4e7ebcbc..4888da9f 100644 --- a/flutter_quill_extensions/lib/embeds/builders.dart +++ b/flutter_quill_extensions/lib/embeds/builders.dart @@ -14,7 +14,7 @@ import 'widgets/image_resizer.dart'; import 'widgets/video_app.dart'; import 'widgets/youtube_video_app.dart'; -class ImageEmbedBuilder implements EmbedBuilder { +class ImageEmbedBuilder extends EmbedBuilder { @override String get key => BlockEmbed.imageType; @@ -24,6 +24,7 @@ class ImageEmbedBuilder implements EmbedBuilder { QuillController controller, base.Embed node, bool readOnly, + bool inline, ) { assert(!kIsWeb, 'Please provide image EmbedBuilder for Web'); @@ -144,7 +145,7 @@ class ImageEmbedBuilder implements EmbedBuilder { } } -class VideoEmbedBuilder implements EmbedBuilder { +class VideoEmbedBuilder extends EmbedBuilder { VideoEmbedBuilder({this.onVideoInit}); final void Function(GlobalKey videoContainerKey)? onVideoInit; @@ -158,6 +159,7 @@ class VideoEmbedBuilder implements EmbedBuilder { QuillController controller, base.Embed node, bool readOnly, + bool inline, ) { assert(!kIsWeb, 'Please provide video EmbedBuilder for Web'); @@ -175,7 +177,7 @@ class VideoEmbedBuilder implements EmbedBuilder { } } -class FormulaEmbedBuilder implements EmbedBuilder { +class FormulaEmbedBuilder extends EmbedBuilder { @override String get key => BlockEmbed.formulaType; @@ -185,6 +187,7 @@ class FormulaEmbedBuilder implements EmbedBuilder { QuillController controller, base.Embed node, bool readOnly, + bool inline, ) { assert(!kIsWeb, 'Please provide formula EmbedBuilder for Web'); diff --git a/lib/src/widgets/delegate.dart b/lib/src/widgets/delegate.dart index 63f446fe..06fc31cb 100644 --- a/lib/src/widgets/delegate.dart +++ b/lib/src/widgets/delegate.dart @@ -8,14 +8,10 @@ import '../models/documents/nodes/leaf.dart'; import '../utils/platform.dart'; import 'controller.dart'; import 'editor.dart'; +import 'embeds.dart'; import 'text_selection.dart'; -typedef EmbedsBuilder = Widget Function( - BuildContext context, - QuillController controller, - Embed node, - bool readOnly, -); +typedef EmbedsBuilder = EmbedBuilder Function(Embed node); typedef CustomStyleBuilder = TextStyle Function(Attribute attribute); diff --git a/lib/src/widgets/editor.dart b/lib/src/widgets/editor.dart index 7246a881..f15210ed 100644 --- a/lib/src/widgets/editor.dart +++ b/lib/src/widgets/editor.dart @@ -363,7 +363,7 @@ class QuillEditor extends StatefulWidget { onSingleLongTapEnd; final Iterable? embedBuilders; - final EmbedsBuilder? unknownEmbedBuilder; + final EmbedBuilder? unknownEmbedBuilder; final CustomStyleBuilder? customStyleBuilder; /// The locale to use for the editor toolbar, defaults to system locale @@ -492,19 +492,7 @@ class QuillEditorState extends State keyboardAppearance: widget.keyboardAppearance, enableInteractiveSelection: widget.enableInteractiveSelection, scrollPhysics: widget.scrollPhysics, - embedBuilder: ( - context, - controller, - node, - readOnly, - ) => - _buildCustomBlockEmbed( - node, - context, - controller, - readOnly, - widget.unknownEmbedBuilder, - ), + embedBuilder: _getEmbedBuilder, linkActionPickerDelegate: widget.linkActionPickerDelegate, customStyleBuilder: widget.customStyleBuilder, floatingCursorDisabled: widget.floatingCursorDisabled, @@ -541,31 +529,19 @@ class QuillEditorState extends State return editor; } - Widget _buildCustomBlockEmbed( - Embed node, - BuildContext context, - QuillController controller, - bool readOnly, - EmbedsBuilder? unknownEmbedBuilder, - ) { + EmbedBuilder _getEmbedBuilder(Embed node) { final builders = widget.embedBuilders; - var _node = node; - // Creates correct node for custom embed - if (node.value.type == BlockEmbed.customType) { - _node = Embed(CustomBlockEmbed.fromJsonString(node.value.data)); - } - if (builders != null) { for (final builder in builders) { - if (builder.key == _node.value.type) { - return builder.build(context, controller, _node, readOnly); + if (builder.key == node.value.type) { + return builder; } } } - if (unknownEmbedBuilder != null) { - return unknownEmbedBuilder(context, controller, _node, readOnly); + if (widget.unknownEmbedBuilder != null) { + return widget.unknownEmbedBuilder!; } throw UnimplementedError( diff --git a/lib/src/widgets/embeds.dart b/lib/src/widgets/embeds.dart index c0262a08..8cfe2657 100644 --- a/lib/src/widgets/embeds.dart +++ b/lib/src/widgets/embeds.dart @@ -7,12 +7,18 @@ import 'controller.dart'; abstract class EmbedBuilder { String get key; + bool get expanded => true; + + WidgetSpan buildWidgetSpan(Widget widget) { + return WidgetSpan(child: widget); + } Widget build( BuildContext context, QuillController controller, leaf.Embed node, bool readOnly, + bool inline, ); } diff --git a/lib/src/widgets/text_line.dart b/lib/src/widgets/text_line.dart index 0f2995bb..3d4e02e0 100644 --- a/lib/src/widgets/text_line.dart +++ b/lib/src/widgets/text_line.dart @@ -10,6 +10,7 @@ import 'package:url_launcher/url_launcher.dart'; import '../models/documents/attribute.dart'; import '../models/documents/nodes/container.dart' as container_node; +import '../models/documents/nodes/embeddable.dart'; import '../models/documents/nodes/leaf.dart'; import '../models/documents/nodes/leaf.dart' as leaf; import '../models/documents/nodes/line.dart'; @@ -132,17 +133,28 @@ class _TextLineState extends State { @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); + if (widget.line.hasEmbed && widget.line.childCount == 1) { - // For video, it is always single child - final embed = widget.line.children.single as Embed; - return EmbedProxy( - widget.embedBuilder( - context, - widget.controller, - embed, - widget.readOnly, - ), - ); + // Single child embeds can be expanded + var embed = widget.line.children.single as Embed; + // Creates correct node for custom embed + if (embed.value.type == BlockEmbed.customType) { + embed = Embed(CustomBlockEmbed.fromJsonString(embed.value.data)); + } + final embedBuilder = widget.embedBuilder(embed); + if (embedBuilder.expanded) { + // Creates correct node for custom embed + + return EmbedProxy( + embedBuilder.build( + context, + widget.controller, + embed, + widget.readOnly, + false, + ), + ); + } } final textSpan = _getTextSpanForWholeLine(context); final strutStyle = StrutStyle.fromTextStyle(textSpan.style!); @@ -173,24 +185,28 @@ class _TextLineState extends State { // The line could contain more than one Embed & more than one Text final textSpanChildren = []; var textNodes = LinkedList(); - for (final child in widget.line.children) { + for (var child in widget.line.children) { if (child is Embed) { if (textNodes.isNotEmpty) { textSpanChildren .add(_buildTextSpan(widget.styles, textNodes, lineStyle)); textNodes = LinkedList(); } - // Here it should be image - final embed = WidgetSpan( - child: EmbedProxy( - widget.embedBuilder( - context, - widget.controller, - child, - widget.readOnly, - ), + // Creates correct node for custom embed + if (child.value.type == BlockEmbed.customType) { + child = Embed(CustomBlockEmbed.fromJsonString(child.value.data)); + } + final embedBuilder = widget.embedBuilder(child); + final embedWidget = EmbedProxy( + embedBuilder.build( + context, + widget.controller, + child, + widget.readOnly, + true, ), ); + final embed = embedBuilder.buildWidgetSpan(embedWidget); textSpanChildren.add(embed); continue; }