extended_text 6.0.4-non-null-safety extended_text: ^6.0.4-non-null-safety copied to clipboard
Extended official text to build special text like inline image or @somebody quickly,it also support custom background,custom over flow and custom selection toolbar and handles.
extended_text #
Extended official text to build special text like inline image or @somebody quickly,it also support custom background,custom over flow and custom selection toolbar and handles.
Table of contents #
Speical Text #
Create Speical Text #
extended text helps to convert your text to speical textSpan quickly.
for example, follwing code show how to create @xxxx speical textSpan.
class AtText extends SpecialText {
AtText(TextStyle textStyle, SpecialTextGestureTapCallback onTap,
{this.showAtBackground = false, this.start})
: super(flag, ' ', textStyle, onTap: onTap);
static const String flag = '@';
final int start;
/// whether show background for @somebody
final bool showAtBackground;
@override
InlineSpan finishText() {
final TextStyle textStyle =
this.textStyle?.copyWith(color: Colors.blue, fontSize: 16.0);
final String atText = toString();
return showAtBackground
? BackgroundTextSpan(
background: Paint()..color = Colors.blue.withOpacity(0.15),
text: atText,
actualText: atText,
start: start,
///caret can move into special text
deleteAll: true,
style: textStyle,
recognizer: (TapGestureRecognizer()
..onTap = () {
if (onTap != null) {
onTap(atText);
}
}))
: SpecialTextSpan(
text: atText,
actualText: atText,
start: start,
style: textStyle,
recognizer: (TapGestureRecognizer()
..onTap = () {
if (onTap != null) {
onTap(atText);
}
}));
}
}
SpecialTextSpanBuilder #
create your SpecialTextSpanBuilder
class MySpecialTextSpanBuilder extends SpecialTextSpanBuilder {
MySpecialTextSpanBuilder({this.showAtBackground = false});
/// whether show background for @somebody
final bool showAtBackground;
@override
TextSpan build(String data,
{TextStyle textStyle, SpecialTextGestureTapCallback onTap}) {
if (kIsWeb) {
return TextSpan(text: data, style: textStyle);
}
return super.build(data, textStyle: textStyle, onTap: onTap);
}
@override
SpecialText createSpecialText(String flag,
{TextStyle textStyle, SpecialTextGestureTapCallback onTap, int index}) {
if (flag == null || flag == '') {
return null;
}
///index is end index of start flag, so text start index should be index-(flag.length-1)
if (isStart(flag, AtText.flag)) {
return AtText(
textStyle,
onTap,
start: index - (AtText.flag.length - 1),
showAtBackground: showAtBackground,
);
} else if (isStart(flag, EmojiText.flag)) {
return EmojiText(textStyle, start: index - (EmojiText.flag.length - 1));
} else if (isStart(flag, DollarText.flag)) {
return DollarText(textStyle, onTap,
start: index - (DollarText.flag.length - 1));
}
return null;
}
}
Image #
ImageSpan #
show inline image by using ImageSpan.
class ImageSpan extends ExtendedWidgetSpan {
ImageSpan(
ImageProvider image, {
Key key,
@required double imageWidth,
@required double imageHeight,
EdgeInsets margin,
int start = 0,
ui.PlaceholderAlignment alignment = ui.PlaceholderAlignment.bottom,
String actualText,
TextBaseline baseline,
BoxFit fit= BoxFit.scaleDown,
ImageLoadingBuilder loadingBuilder,
ImageFrameBuilder frameBuilder,
String semanticLabel,
bool excludeFromSemantics = false,
Color color,
BlendMode colorBlendMode,
AlignmentGeometry imageAlignment = Alignment.center,
ImageRepeat repeat = ImageRepeat.noRepeat,
Rect centerSlice,
bool matchTextDirection = false,
bool gaplessPlayback = false,
FilterQuality filterQuality = FilterQuality.low,
GestureTapCallback onTap,
HitTestBehavior behavior = HitTestBehavior.deferToChild,
})
parameter | description | default |
---|---|---|
image | The image to display(ImageProvider). | - |
imageWidth | The width of image(not include margin) | required |
imageHeight | The height of image(not include margin) | required |
margin | The margin of image | - |
actualText | Actual text, take care of it when enable selection,something likes "[love]" | '\uFFFC' |
start | Start index of text,take care of it when enable selection. | 0 |
Selection #
parameter | description | default |
---|---|---|
selectionEnabled | Whether enable selection | false |
selectionColor | Color of selection | Theme.of(context).textSelectionColor |
dragStartBehavior | DragStartBehavior for text selection | DragStartBehavior.start |
textSelectionControls | An interface for building the selection UI, to be provided by the implementor of the toolbar widget or handle widget | extendedMaterialTextSelectionControls/extendedCupertinoTextSelectionControls |
TextSelectionControls #
default value of textSelectionControls are MaterialExtendedTextSelectionControls/CupertinoExtendedTextSelectionControls
override buildToolbar or buildHandle to custom your toolbar widget or handle widget
class MyExtendedMaterialTextSelectionControls
extends ExtendedMaterialTextSelectionControls {
MyExtendedMaterialTextSelectionControls();
@override
Widget buildToolbar(
BuildContext context,
Rect globalEditableRegion,
double textLineHeight,
Offset selectionMidpoint,
List<TextSelectionPoint> endpoints,
TextSelectionDelegate delegate,
) {}
@override
Widget buildHandle(
BuildContext context, TextSelectionHandleType type, double textHeight) {
}
}
Control ToolBar Handle #
contain your page into ExtendedTextSelectionPointerHandler, so you can control toolbar and handle.
Default Behavior
set your page as child of ExtendedTextSelectionPointerHandler
return ExtendedTextSelectionPointerHandler(
//default behavior
child: result,
);
- tap region outside of extended text, hide toolbar and handle
- scorll, hide toolbar and handle
Custom Behavior
get selectionStates(ExtendedTextSelectionState) by builder call back, and handle by your self.
return ExtendedTextSelectionPointerHandler(
//default behavior
// child: result,
//custom your behavior
builder: (states) {
return Listener(
child: result,
behavior: HitTestBehavior.translucent,
onPointerDown: (value) {
for (var state in states) {
if (!state.containsPosition(value.position)) {
//clear other selection
state.clearSelection();
}
}
},
onPointerMove: (value) {
//clear other selection
for (var state in states) {
state.clearSelection();
}
},
);
},
);
Custom Background #
refer to issues 24335/24337 about background
BackgroundTextSpan(
text:
"This text has nice background with borderradius,no mattter how many line,it likes nice",
background: Paint()..color = Colors.indigo,
clipBorderRadius: BorderRadius.all(Radius.circular(3.0))),
parameter | description | default |
---|---|---|
background | Background painter | - |
clipBorderRadius | Clip BorderRadius | - |
paintBackground | Paint background call back, you can paint background by self | - |
Custom Overflow #
refer to issue 26748
parameter | description | default |
---|---|---|
child | The widget of TextOverflow. | @required |
maxHeight | The maxHeight of [TextOverflowWidget], default is preferredLineHeight. | preferredLineHeight |
align | The Align of [TextOverflowWidget], left/right. | right |
position | The position which TextOverflowWidget should be shown. | TextOverflowPosition.end |
ExtendedText(
overflowWidget: TextOverflowWidget(
position: TextOverflowPosition.end,
align: TextOverflowAlign.center,
// just for debug
debugOverflowRectColor: Colors.red.withOpacity(0.1),
child: Container(
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('\u2026 '),
InkWell(
child: const Text(
'more',
),
onTap: () {
launch(
'https://github.com/fluttercandies/extended_text');
},
)
],
),
),
),
)
Join Zero-Width Space #
refer to issue 18761
if [ExtendedText.joinZeroWidthSpace] is true, it will join '\u{200B}' into text, make line breaking and overflow style better.
ExtendedText(
joinZeroWidthSpace: true,
)
or you can convert by following method:
- String
String input='abc'.joinChar();
- InlineSpan
InlineSpan innerTextSpan;
innerTextSpan = joinChar(
innerTextSpan,
Accumulator(),
zeroWidthSpace,
);
Take care of following things:
-
the word is not a word, it will not working when you want to double tap to select a word.
-
text is changed, if [ExtendedText.selectionEnabled] is true, you should override TextSelectionControls and remove zeroWidthSpace.
class MyTextSelectionControls extends TextSelectionControls {
@override
void handleCopy(TextSelectionDelegate delegate,
ClipboardStatusNotifier? clipboardStatus) {
final TextEditingValue value = delegate.textEditingValue;
String data = value.selection.textInside(value.text);
// remove zeroWidthSpace
data = data.replaceAll(zeroWidthSpace, '');
Clipboard.setData(ClipboardData(
text: value.selection.textInside(value.text),
));
clipboardStatus?.update();
delegate.textEditingValue = TextEditingValue(
text: value.text,
selection: TextSelection.collapsed(offset: value.selection.end),
);
delegate.bringIntoView(delegate.textEditingValue.selection.extent);
delegate.hideToolbar();
}
}