Flutter 支援圖片以及特殊文字的輸入框(一)使用方法
extended_text_field 相關文章
- Flutter 支援圖片以及特殊文字的輸入框(一)使用方法
- Flutter 支援圖片以及特殊文字的輸入框(二)實現過程
最近有客戶Alex提出Flutter輸入框需要支援支援圖片和特殊文字樣式,就跟QQ,微信,微博一樣,可以插入表情,@xxx或者一個話題. 每天追更,每天都在群裡@我(FlutterCandies QQ群:181398081),就差點就把他禁言了。但是我沒有這樣做,客戶就是上帝,因為之前做過Extended Text,假裝感覺應該很簡單,悄悄在空閒的時候就動手做起來了。

本篇只介紹下用法,下一篇再講講開發中的辛酸歷程,先上效果圖。

使用步驟
-
關注,點贊,轉發,送飛機
-
使用ExtendedTextField
-
Flutter Candies全家桶
關注,點贊,轉發,送飛機
pub 搜尋 extended_text_field
github 地址: extended_text_field
定義自己的特殊文字

比如表情
我這裡定義的格式是比如[1],就代表是表情1(就是你對應的表情圖片).
class EmojiText extends SpecialText { static const String flag = "["; final int start; EmojiText(TextStyle textStyle, {this.start}) : super(EmojiText.flag, "]", textStyle); @override TextSpan finishText() { // TODO: implement finishText var key = toString(); if (EmojiUitl.instance.emojiMap.containsKey(key)) { //fontsize id define image height //size = 30.0/26.0 * fontSize final double size = 20.0; ///fontSize 26 and text height =30.0 //final double fontSize = 26.0; return ImageSpan(AssetImage(EmojiUitl.instance.emojiMap[key]), actualText: key, imageWidth: size, imageHeight: size, start: start, deleteAll: true, fit: BoxFit.fill, margin: EdgeInsets.only(left: 2.0, top: 2.0, right: 2.0)); } return TextSpan(text: toString(), style: textStyle); } } class EmojiUitl { final Map<String, String> _emojiMap = new Map<String, String>(); Map<String, String> get emojiMap => _emojiMap; final String _emojiFilePath = "assets"; static EmojiUitl _instance; static EmojiUitl get instance { if (_instance == null) _instance = new EmojiUitl._(); return _instance; } EmojiUitl._() { for (int i = 1; i < 49; i++) { _emojiMap["[$i]"] = "$_emojiFilePath/$i.png"; } } } 複製程式碼
再舉一個

比如 @法的空間
以@為開始標誌,空格為結束標誌
class AtText extends SpecialText { static const String flag = "@"; final int start; /// whether show background for @somebody final bool showAtBackground; final BuilderType type; AtText(TextStyle textStyle, SpecialTextGestureTapCallback onTap, {this.showAtBackground: false, this.type, this.start}) : super(flag, " ", textStyle, onTap: onTap); @override TextSpan finishText() { // TODO: implement finishText TextStyle textStyle = this.textStyle?.copyWith(color: Colors.blue, fontSize: 16.0); final String atText = toString(); if (type == BuilderType.extendedText) return TextSpan( text: atText, style: textStyle, recognizer: TapGestureRecognizer() ..onTap = () { if (onTap != null) onTap(atText); }); return showAtBackground ? BackgroundTextSpan( background: Paint()..color = Colors.blue.withOpacity(0.15), text: atText, actualText: atText, start: start, deleteAll: false, style: textStyle, recognizer: type == BuilderType.extendedText ? (TapGestureRecognizer() ..onTap = () { if (onTap != null) onTap(atText); }) : null) : SpecialTextSpan( text: atText, actualText: atText, start: start, deleteAll: false, style: textStyle, recognizer: type == BuilderType.extendedText ? (TapGestureRecognizer() ..onTap = () { if (onTap != null) onTap(atText); }) : null); } } 複製程式碼
定義文字解析幫助類
必須實現createSpecialText方法,這樣才知道你有哪些特殊文字
一個是build的方法,可選實現。如果你自己實現了要注意,特殊TextSpan必須放在返回的TextSpan的children裡面,我只會遍歷這一層,不會再去查詢children的children了。
class MySpecialTextSpanBuilder extends SpecialTextSpanBuilder { /// whether show background for @somebody final bool showAtBackground; final BuilderType type; MySpecialTextSpanBuilder( {this.showAtBackground: false, this.type: BuilderType.extendedText}); @override TextSpan build(String data, {TextStyle textStyle, onTap}) { // TODO: implement build var textSpan = super.build(data, textStyle: textStyle, onTap: onTap); //for performance, make sure your all SpecialTextSpan are only in textSpan.children //extended_text_field will only check SpecialTextSpan in textSpan.children return textSpan; } @override SpecialText createSpecialText(String flag, {TextStyle textStyle, SpecialTextGestureTapCallback onTap, int index}) { if (flag == null || flag == "") return null; // TODO: implement createSpecialText ///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, type: type); } 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), type: type); } return null; } } enum BuilderType { extendedText, extendedTextField } 複製程式碼
使用ExtendedTextField
是不是炒雞簡單,這樣你的文字就會自動轉換為對應的特殊文字型別了
ExtendedTextField( specialTextSpanBuilder: MySpecialTextSpanBuilder( showAtBackground: true, type: BuilderType.extendedTextField), 複製程式碼
限制
跟 readme 上面講的一樣,有三種限制。
- 不支援文字從右到左,也就是不支援TextDirection.rtl。原因是TextPainter 給的圖片的位置,非常奇怪,完全沒法搞。當然我會繼續跟進,也許哪天官方修好了呢?
- 不支援那種密碼的輸入樣式解析成特殊TextSpan,也就是不支援obscureText 為true。沒啥好解釋,文字都變成******了,也沒必要解析了。
- 程式碼是基於flutter 版本1.5.7,可能在不同的flutter 版本下面會出現編譯錯誤,如果出現,希望老闆們能根據自己的版本進行更正。我這邊不太可能都適配到每個flutter 版本,我會盡量讓 extended_text_field 在flutter 的穩定版本上面沒有錯誤,希望諒解。
最後放上 extended_text_field ,如果你有什麼不明白或者對這個方案有什麼改進的地方,請告訴我,歡迎加入 Flutter Candies ,一起生產可愛的Flutter 小糖果(QQ群:181398081)
Flutter Candies全家桶
最最後放上 Flutter Candies 全家桶,真香。
custom flutter candies(widgets) for you to easily build flutter app, enjoy it.
