Skip to content

Commit b7951b0

Browse files
authored
allow widgets to override widget span properties (singerdmx#1141)
1 parent 34cba17 commit b7951b0

File tree

9 files changed

+73
-67
lines changed

9 files changed

+73
-67
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ After that, we need to map this "notes" type into a widget. In that case, I used
241241
Don't forget to add this method to the `QuillEditor` after that!
242242

243243
```dart
244-
class NotesEmbedBuilder implements EmbedBuilder {
244+
class NotesEmbedBuilder extends EmbedBuilder {
245245
NotesEmbedBuilder({required this.addEditNote});
246246
247247
Future<void> Function(BuildContext context, {Document? document}) addEditNote;
@@ -255,6 +255,7 @@ class NotesEmbedBuilder implements EmbedBuilder {
255255
QuillController controller,
256256
Embed node,
257257
bool readOnly,
258+
bool inline,
258259
) {
259260
final notes = NotesBlockEmbed(node.value.data).document;
260261

doc_cn.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ class NotesBlockEmbed extends CustomBlockEmbed {
246246
在这里我们使用 `ListTile` 来渲染它,并使用 `onTap` 方法来编辑内容,最后不要忘记将此方法添加到 `QuillEditor`
247247

248248
```dart
249-
class NotesEmbedBuilder implements EmbedBuilder {
249+
class NotesEmbedBuilder extends EmbedBuilder {
250250
NotesEmbedBuilder({required this.addEditNote});
251251
252252
Future<void> Function(BuildContext context, {Document? document}) addEditNote;
@@ -260,6 +260,7 @@ class NotesEmbedBuilder implements EmbedBuilder {
260260
QuillController controller,
261261
Embed node,
262262
bool readOnly,
263+
bool inline,
263264
) {
264265
final notes = NotesBlockEmbed(node.value.data).document;
265266

example/lib/pages/home_page.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ class _HomePageState extends State<HomePage> {
487487
}
488488
}
489489

490-
class NotesEmbedBuilder implements EmbedBuilder {
490+
class NotesEmbedBuilder extends EmbedBuilder {
491491
NotesEmbedBuilder({required this.addEditNote});
492492

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

example/lib/universal_ui/universal_ui.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class UniversalUI {
2727

2828
var ui = UniversalUI();
2929

30-
class ImageEmbedBuilderWeb implements EmbedBuilder {
30+
class ImageEmbedBuilderWeb extends EmbedBuilder {
3131
@override
3232
String get key => BlockEmbed.imageType;
3333

@@ -37,6 +37,7 @@ class ImageEmbedBuilderWeb implements EmbedBuilder {
3737
QuillController controller,
3838
Embed node,
3939
bool readOnly,
40+
bool inline,
4041
) {
4142
final imageUrl = node.value.data;
4243
if (isImageBase64(imageUrl)) {
@@ -68,13 +69,18 @@ class ImageEmbedBuilderWeb implements EmbedBuilder {
6869
}
6970
}
7071

71-
class VideoEmbedBuilderWeb implements EmbedBuilder {
72+
class VideoEmbedBuilderWeb extends EmbedBuilder {
7273
@override
7374
String get key => BlockEmbed.videoType;
7475

7576
@override
76-
Widget build(BuildContext context, QuillController controller, Embed node,
77-
bool readOnly) {
77+
Widget build(
78+
BuildContext context,
79+
QuillController controller,
80+
Embed node,
81+
bool readOnly,
82+
bool inline,
83+
) {
7884
var videoUrl = node.value.data;
7985
if (videoUrl.contains('youtube.com') || videoUrl.contains('youtu.be')) {
8086
final youtubeID = YoutubePlayer.convertUrlToId(videoUrl);

flutter_quill_extensions/lib/embeds/builders.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import 'widgets/image_resizer.dart';
1414
import 'widgets/video_app.dart';
1515
import 'widgets/youtube_video_app.dart';
1616

17-
class ImageEmbedBuilder implements EmbedBuilder {
17+
class ImageEmbedBuilder extends EmbedBuilder {
1818
@override
1919
String get key => BlockEmbed.imageType;
2020

@@ -24,6 +24,7 @@ class ImageEmbedBuilder implements EmbedBuilder {
2424
QuillController controller,
2525
base.Embed node,
2626
bool readOnly,
27+
bool inline,
2728
) {
2829
assert(!kIsWeb, 'Please provide image EmbedBuilder for Web');
2930

@@ -144,7 +145,7 @@ class ImageEmbedBuilder implements EmbedBuilder {
144145
}
145146
}
146147

147-
class VideoEmbedBuilder implements EmbedBuilder {
148+
class VideoEmbedBuilder extends EmbedBuilder {
148149
VideoEmbedBuilder({this.onVideoInit});
149150

150151
final void Function(GlobalKey videoContainerKey)? onVideoInit;
@@ -158,6 +159,7 @@ class VideoEmbedBuilder implements EmbedBuilder {
158159
QuillController controller,
159160
base.Embed node,
160161
bool readOnly,
162+
bool inline,
161163
) {
162164
assert(!kIsWeb, 'Please provide video EmbedBuilder for Web');
163165

@@ -175,7 +177,7 @@ class VideoEmbedBuilder implements EmbedBuilder {
175177
}
176178
}
177179

178-
class FormulaEmbedBuilder implements EmbedBuilder {
180+
class FormulaEmbedBuilder extends EmbedBuilder {
179181
@override
180182
String get key => BlockEmbed.formulaType;
181183

@@ -185,6 +187,7 @@ class FormulaEmbedBuilder implements EmbedBuilder {
185187
QuillController controller,
186188
base.Embed node,
187189
bool readOnly,
190+
bool inline,
188191
) {
189192
assert(!kIsWeb, 'Please provide formula EmbedBuilder for Web');
190193

lib/src/widgets/delegate.dart

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,10 @@ import '../models/documents/nodes/leaf.dart';
88
import '../utils/platform.dart';
99
import 'controller.dart';
1010
import 'editor.dart';
11+
import 'embeds.dart';
1112
import 'text_selection.dart';
1213

13-
typedef EmbedsBuilder = Widget Function(
14-
BuildContext context,
15-
QuillController controller,
16-
Embed node,
17-
bool readOnly,
18-
);
14+
typedef EmbedsBuilder = EmbedBuilder Function(Embed node);
1915

2016
typedef CustomStyleBuilder = TextStyle Function(Attribute attribute);
2117

lib/src/widgets/editor.dart

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ class QuillEditor extends StatefulWidget {
363363
onSingleLongTapEnd;
364364

365365
final Iterable<EmbedBuilder>? embedBuilders;
366-
final EmbedsBuilder? unknownEmbedBuilder;
366+
final EmbedBuilder? unknownEmbedBuilder;
367367
final CustomStyleBuilder? customStyleBuilder;
368368

369369
/// The locale to use for the editor toolbar, defaults to system locale
@@ -492,19 +492,7 @@ class QuillEditorState extends State<QuillEditor>
492492
keyboardAppearance: widget.keyboardAppearance,
493493
enableInteractiveSelection: widget.enableInteractiveSelection,
494494
scrollPhysics: widget.scrollPhysics,
495-
embedBuilder: (
496-
context,
497-
controller,
498-
node,
499-
readOnly,
500-
) =>
501-
_buildCustomBlockEmbed(
502-
node,
503-
context,
504-
controller,
505-
readOnly,
506-
widget.unknownEmbedBuilder,
507-
),
495+
embedBuilder: _getEmbedBuilder,
508496
linkActionPickerDelegate: widget.linkActionPickerDelegate,
509497
customStyleBuilder: widget.customStyleBuilder,
510498
floatingCursorDisabled: widget.floatingCursorDisabled,
@@ -541,31 +529,19 @@ class QuillEditorState extends State<QuillEditor>
541529
return editor;
542530
}
543531

544-
Widget _buildCustomBlockEmbed(
545-
Embed node,
546-
BuildContext context,
547-
QuillController controller,
548-
bool readOnly,
549-
EmbedsBuilder? unknownEmbedBuilder,
550-
) {
532+
EmbedBuilder _getEmbedBuilder(Embed node) {
551533
final builders = widget.embedBuilders;
552534

553-
var _node = node;
554-
// Creates correct node for custom embed
555-
if (node.value.type == BlockEmbed.customType) {
556-
_node = Embed(CustomBlockEmbed.fromJsonString(node.value.data));
557-
}
558-
559535
if (builders != null) {
560536
for (final builder in builders) {
561-
if (builder.key == _node.value.type) {
562-
return builder.build(context, controller, _node, readOnly);
537+
if (builder.key == node.value.type) {
538+
return builder;
563539
}
564540
}
565541
}
566542

567-
if (unknownEmbedBuilder != null) {
568-
return unknownEmbedBuilder(context, controller, _node, readOnly);
543+
if (widget.unknownEmbedBuilder != null) {
544+
return widget.unknownEmbedBuilder!;
569545
}
570546

571547
throw UnimplementedError(

lib/src/widgets/embeds.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@ import 'controller.dart';
77

88
abstract class EmbedBuilder {
99
String get key;
10+
bool get expanded => true;
11+
12+
WidgetSpan buildWidgetSpan(Widget widget) {
13+
return WidgetSpan(child: widget);
14+
}
1015

1116
Widget build(
1217
BuildContext context,
1318
QuillController controller,
1419
leaf.Embed node,
1520
bool readOnly,
21+
bool inline,
1622
);
1723
}
1824

lib/src/widgets/text_line.dart

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:url_launcher/url_launcher.dart';
1010

1111
import '../models/documents/attribute.dart';
1212
import '../models/documents/nodes/container.dart' as container_node;
13+
import '../models/documents/nodes/embeddable.dart';
1314
import '../models/documents/nodes/leaf.dart';
1415
import '../models/documents/nodes/leaf.dart' as leaf;
1516
import '../models/documents/nodes/line.dart';
@@ -132,17 +133,28 @@ class _TextLineState extends State<TextLine> {
132133
@override
133134
Widget build(BuildContext context) {
134135
assert(debugCheckHasMediaQuery(context));
136+
135137
if (widget.line.hasEmbed && widget.line.childCount == 1) {
136-
// For video, it is always single child
137-
final embed = widget.line.children.single as Embed;
138-
return EmbedProxy(
139-
widget.embedBuilder(
140-
context,
141-
widget.controller,
142-
embed,
143-
widget.readOnly,
144-
),
145-
);
138+
// Single child embeds can be expanded
139+
var embed = widget.line.children.single as Embed;
140+
// Creates correct node for custom embed
141+
if (embed.value.type == BlockEmbed.customType) {
142+
embed = Embed(CustomBlockEmbed.fromJsonString(embed.value.data));
143+
}
144+
final embedBuilder = widget.embedBuilder(embed);
145+
if (embedBuilder.expanded) {
146+
// Creates correct node for custom embed
147+
148+
return EmbedProxy(
149+
embedBuilder.build(
150+
context,
151+
widget.controller,
152+
embed,
153+
widget.readOnly,
154+
false,
155+
),
156+
);
157+
}
146158
}
147159
final textSpan = _getTextSpanForWholeLine(context);
148160
final strutStyle = StrutStyle.fromTextStyle(textSpan.style!);
@@ -173,24 +185,28 @@ class _TextLineState extends State<TextLine> {
173185
// The line could contain more than one Embed & more than one Text
174186
final textSpanChildren = <InlineSpan>[];
175187
var textNodes = LinkedList<Node>();
176-
for (final child in widget.line.children) {
188+
for (var child in widget.line.children) {
177189
if (child is Embed) {
178190
if (textNodes.isNotEmpty) {
179191
textSpanChildren
180192
.add(_buildTextSpan(widget.styles, textNodes, lineStyle));
181193
textNodes = LinkedList<Node>();
182194
}
183-
// Here it should be image
184-
final embed = WidgetSpan(
185-
child: EmbedProxy(
186-
widget.embedBuilder(
187-
context,
188-
widget.controller,
189-
child,
190-
widget.readOnly,
191-
),
195+
// Creates correct node for custom embed
196+
if (child.value.type == BlockEmbed.customType) {
197+
child = Embed(CustomBlockEmbed.fromJsonString(child.value.data));
198+
}
199+
final embedBuilder = widget.embedBuilder(child);
200+
final embedWidget = EmbedProxy(
201+
embedBuilder.build(
202+
context,
203+
widget.controller,
204+
child,
205+
widget.readOnly,
206+
true,
192207
),
193208
);
209+
final embed = embedBuilder.buildWidgetSpan(embedWidget);
194210
textSpanChildren.add(embed);
195211
continue;
196212
}

0 commit comments

Comments
 (0)