Skip to content

Commit c3106bb

Browse files
[SuperTextField] - Add support for inline widgets (Resolves #2507) (#2520)
1 parent 0b8ec65 commit c3106bb

20 files changed

+730
-11
lines changed

super_editor/lib/src/super_textfield/android/android_textfield.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:attributed_text/attributed_text.dart';
12
import 'package:flutter/material.dart';
23
import 'package:flutter/services.dart';
34
import 'package:follow_the_leader/follow_the_leader.dart';
@@ -35,6 +36,7 @@ class SuperAndroidTextField extends StatefulWidget {
3536
this.textController,
3637
this.textAlign,
3738
this.textStyleBuilder = defaultTextFieldStyleBuilder,
39+
this.inlineWidgetBuilders = const [],
3840
this.hintBehavior = HintBehavior.displayHintUntilFocus,
3941
this.hintBuilder,
4042
this.minLines,
@@ -73,6 +75,9 @@ class SuperAndroidTextField extends StatefulWidget {
7375
/// [textController] based on the attributions in that content.
7476
final AttributionStyleBuilder textStyleBuilder;
7577

78+
/// {@macro super_text_field_inline_widget_builders}
79+
final InlineWidgetBuilderChain inlineWidgetBuilders;
80+
7681
/// Policy for when the hint should be displayed.
7782
final HintBehavior hintBehavior;
7883

@@ -631,7 +636,7 @@ class SuperAndroidTextFieldState extends State<SuperAndroidTextField>
631636

632637
Widget _buildSelectableText() {
633638
final textSpan = _textEditingController.text.isNotEmpty
634-
? _textEditingController.text.computeTextSpan(widget.textStyleBuilder)
639+
? _textEditingController.text.computeInlineSpan(context, widget.textStyleBuilder, widget.inlineWidgetBuilders)
635640
: TextSpan(text: "", style: widget.textStyleBuilder({}));
636641

637642
return Directionality(

super_editor/lib/src/super_textfield/desktop/desktop_textfield.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ class SuperDesktopTextField extends StatefulWidget {
5151
this.tapRegionGroupId,
5252
this.textController,
5353
this.textStyleBuilder = defaultTextFieldStyleBuilder,
54-
this.textAlign,
54+
this.inlineWidgetBuilders = const [],
55+
this.textAlign = TextAlign.left,
5556
this.hintBehavior = HintBehavior.displayHintUntilFocus,
5657
this.hintBuilder,
5758
this.selectionHighlightStyle = const SelectionHighlightStyle(
@@ -92,6 +93,9 @@ class SuperDesktopTextField extends StatefulWidget {
9293
/// [textController] based on the attributions in that content.
9394
final AttributionStyleBuilder textStyleBuilder;
9495

96+
/// {@macro super_text_field_inline_widget_builders}
97+
final InlineWidgetBuilderChain inlineWidgetBuilders;
98+
9599
/// Policy for when the hint should be displayed.
96100
final HintBehavior hintBehavior;
97101

@@ -525,7 +529,7 @@ class SuperDesktopTextFieldState extends State<SuperDesktopTextField> implements
525529
textDirection: _textDirection,
526530
child: SuperText(
527531
key: _textKey,
528-
richText: _controller.text.computeTextSpan(widget.textStyleBuilder),
532+
richText: _controller.text.computeInlineSpan(context, widget.textStyleBuilder, widget.inlineWidgetBuilders),
529533
textAlign: _textAlign,
530534
textDirection: _textDirection,
531535
textScaler: _textScaler,

super_editor/lib/src/super_textfield/infrastructure/attributed_text_editing_controller.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,7 @@ class AttributedTextEditingController with ChangeNotifier {
655655
notifyListeners();
656656
}
657657

658+
@Deprecated('Use text.computeInlineSpan() instead, which adds support for inline widgets.')
658659
TextSpan buildTextSpan(AttributionStyleBuilder styleBuilder) {
659660
return text.computeTextSpan(styleBuilder);
660661
}

super_editor/lib/src/super_textfield/ios/ios_textfield.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ class SuperIOSTextField extends StatefulWidget {
4242
this.tapHandlers = const [],
4343
this.textController,
4444
this.textStyleBuilder = defaultTextFieldStyleBuilder,
45-
this.textAlign,
45+
this.inlineWidgetBuilders = const [],
46+
this.textAlign = TextAlign.left,
4647
this.padding,
4748
this.hintBehavior = HintBehavior.displayHintUntilFocus,
4849
this.hintBuilder,
@@ -83,6 +84,9 @@ class SuperIOSTextField extends StatefulWidget {
8384
/// [textController] based on the attributions in that content.
8485
final AttributionStyleBuilder textStyleBuilder;
8586

87+
/// {@macro super_text_field_inline_widget_builders}
88+
final InlineWidgetBuilderChain inlineWidgetBuilders;
89+
8690
/// Padding placed around the text content of this text field, but within the
8791
/// scrollable viewport.
8892
final EdgeInsets? padding;
@@ -630,9 +634,8 @@ class SuperIOSTextFieldState extends State<SuperIOSTextField>
630634
}
631635

632636
Widget _buildSelectableText() {
633-
final textSpan = _textEditingController.text.isNotEmpty
634-
? _textEditingController.text.computeTextSpan(widget.textStyleBuilder)
635-
: AttributedText().computeTextSpan(widget.textStyleBuilder);
637+
final textSpan = _textEditingController.text //
638+
.computeInlineSpan(context, widget.textStyleBuilder, widget.inlineWidgetBuilders);
636639

637640
CaretStyle caretStyle = widget.caretStyle;
638641

super_editor/lib/src/super_textfield/super_textfield.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class SuperTextField extends StatefulWidget {
6262
this.textController,
6363
this.textAlign,
6464
this.textStyleBuilder = defaultTextFieldStyleBuilder,
65+
this.inlineWidgetBuilders = const [],
6566
this.hintBehavior = HintBehavior.displayHintUntilFocus,
6667
this.hintBuilder,
6768
this.controlsColor,
@@ -107,6 +108,14 @@ class SuperTextField extends StatefulWidget {
107108
/// [textController] based on the attributions in that content.
108109
final AttributionStyleBuilder textStyleBuilder;
109110

111+
/// {@template super_text_field_inline_widget_builders}
112+
/// A Chain of Responsibility that's used to build inline widgets.
113+
///
114+
/// The first builder in the chain to return a non-null `Widget` will be
115+
/// used for a given inline placeholder.
116+
/// {@endtemplate}
117+
final InlineWidgetBuilderChain inlineWidgetBuilders;
118+
110119
/// Policy for when the hint should be displayed.
111120
final HintBehavior hintBehavior;
112121

@@ -364,6 +373,7 @@ class SuperTextFieldState extends State<SuperTextField> implements ImeInputOwner
364373
textController: _controller,
365374
textAlign: widget.textAlign,
366375
textStyleBuilder: widget.textStyleBuilder,
376+
inlineWidgetBuilders: widget.inlineWidgetBuilders,
367377
hintBehavior: widget.hintBehavior,
368378
hintBuilder: widget.hintBuilder,
369379
selectionHighlightStyle: SelectionHighlightStyle(
@@ -398,6 +408,7 @@ class SuperTextFieldState extends State<SuperTextField> implements ImeInputOwner
398408
textController: _controller,
399409
textAlign: widget.textAlign,
400410
textStyleBuilder: widget.textStyleBuilder,
411+
inlineWidgetBuilders: widget.inlineWidgetBuilders,
401412
hintBehavior: widget.hintBehavior,
402413
hintBuilder: widget.hintBuilder,
403414
caretStyle: widget.caretStyle ??
@@ -427,6 +438,7 @@ class SuperTextFieldState extends State<SuperTextField> implements ImeInputOwner
427438
textController: _controller,
428439
textAlign: widget.textAlign,
429440
textStyleBuilder: widget.textStyleBuilder,
441+
inlineWidgetBuilders: widget.inlineWidgetBuilders,
430442
padding: widget.padding,
431443
hintBehavior: widget.hintBehavior,
432444
hintBuilder: widget.hintBuilder,

super_editor/test/super_textfield/super_textfield_attributions_test.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ void main() {
3131
);
3232

3333
// Ensure the text is colored orange.
34-
expect(
35-
SuperTextFieldInspector.findRichText().style!.color,
36-
Colors.orange,
37-
);
34+
for (int i = 0; i <= 9; i++) {
35+
expect(
36+
SuperTextFieldInspector.findRichText().getSpanForPosition(TextPosition(offset: i))!.style!.color,
37+
Colors.orange,
38+
);
39+
}
3840
});
3941

4042
testWidgetsOnAllPlatforms("to partial text", (tester) async {

0 commit comments

Comments
 (0)