Skip to content

Commit

Permalink
allow widgets to override widget span properties (#1141)
Browse files Browse the repository at this point in the history
  • Loading branch information
BertrandBev committed Mar 25, 2023
1 parent 34cba17 commit b7951b0
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 67 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> Function(BuildContext context, {Document? document}) addEditNote;
Expand All @@ -255,6 +255,7 @@ class NotesEmbedBuilder implements EmbedBuilder {
QuillController controller,
Embed node,
bool readOnly,
bool inline,
) {
final notes = NotesBlockEmbed(node.value.data).document;
Expand Down
3 changes: 2 additions & 1 deletion doc_cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> Function(BuildContext context, {Document? document}) addEditNote;
Expand All @@ -260,6 +260,7 @@ class NotesEmbedBuilder implements EmbedBuilder {
QuillController controller,
Embed node,
bool readOnly,
bool inline,
) {
final notes = NotesBlockEmbed(node.value.data).document;
Expand Down
3 changes: 2 additions & 1 deletion example/lib/pages/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ class _HomePageState extends State<HomePage> {
}
}

class NotesEmbedBuilder implements EmbedBuilder {
class NotesEmbedBuilder extends EmbedBuilder {
NotesEmbedBuilder({required this.addEditNote});

Future<void> Function(BuildContext context, {Document? document}) addEditNote;
Expand All @@ -501,6 +501,7 @@ class NotesEmbedBuilder implements EmbedBuilder {
QuillController controller,
Embed node,
bool readOnly,
bool inline,
) {
final notes = NotesBlockEmbed(node.value.data).document;

Expand Down
14 changes: 10 additions & 4 deletions example/lib/universal_ui/universal_ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class UniversalUI {

var ui = UniversalUI();

class ImageEmbedBuilderWeb implements EmbedBuilder {
class ImageEmbedBuilderWeb extends EmbedBuilder {
@override
String get key => BlockEmbed.imageType;

Expand All @@ -37,6 +37,7 @@ class ImageEmbedBuilderWeb implements EmbedBuilder {
QuillController controller,
Embed node,
bool readOnly,
bool inline,
) {
final imageUrl = node.value.data;
if (isImageBase64(imageUrl)) {
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 6 additions & 3 deletions flutter_quill_extensions/lib/embeds/builders.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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');

Expand Down Expand Up @@ -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;
Expand All @@ -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');

Expand All @@ -175,7 +177,7 @@ class VideoEmbedBuilder implements EmbedBuilder {
}
}

class FormulaEmbedBuilder implements EmbedBuilder {
class FormulaEmbedBuilder extends EmbedBuilder {
@override
String get key => BlockEmbed.formulaType;

Expand All @@ -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');

Expand Down
8 changes: 2 additions & 6 deletions lib/src/widgets/delegate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
38 changes: 7 additions & 31 deletions lib/src/widgets/editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ class QuillEditor extends StatefulWidget {
onSingleLongTapEnd;

final Iterable<EmbedBuilder>? embedBuilders;
final EmbedsBuilder? unknownEmbedBuilder;
final EmbedBuilder? unknownEmbedBuilder;
final CustomStyleBuilder? customStyleBuilder;

/// The locale to use for the editor toolbar, defaults to system locale
Expand Down Expand Up @@ -492,19 +492,7 @@ class QuillEditorState extends State<QuillEditor>
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,
Expand Down Expand Up @@ -541,31 +529,19 @@ class QuillEditorState extends State<QuillEditor>
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(
Expand Down
6 changes: 6 additions & 0 deletions lib/src/widgets/embeds.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
}

Expand Down
56 changes: 36 additions & 20 deletions lib/src/widgets/text_line.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -132,17 +133,28 @@ class _TextLineState extends State<TextLine> {
@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!);
Expand Down Expand Up @@ -173,24 +185,28 @@ class _TextLineState extends State<TextLine> {
// The line could contain more than one Embed & more than one Text
final textSpanChildren = <InlineSpan>[];
var textNodes = LinkedList<Node>();
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<Node>();
}
// 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;
}
Expand Down

0 comments on commit b7951b0

Please sign in to comment.