前语
该文章只记载与工作中碰到的问题,Flutter 版本为 1.2.6 。有更多解决办法欢迎评论学习。
背景
产品想要在产品查找界面实现实时查找。用户在输入产品名称的时分 就主动查找产品信息。
问题
ios机型在原生键盘中文场景下,在输入字母拼音时onChanged:办法在iOS上会实时回调,拼音也会被查找。假如输入过快,因为接口异步回调导致内容展现异常。
错误解决办法
- 输入中文时约束字母拼音在输入框内的显现,待挑选中文后再显现,此刻回调onChanged(不推荐运用,产品并非都是中文)
- 运用dart自带特点isComposingRangeValid (听说 Flutter 2.0可用)
#state.dart
TextEditingController _controller;
_controller = TextEditingController.fromValue(TextEditingValue(
text: searchText,
// 坚持光标在最后
selection: TextSelection.fromPosition(TextPosition(
affinity: TextAffinity.downstream, offset: searchText.length))))
#View.dart
Expanded(
flex: 1,
child: TextField(
decoration: new InputDecoration(
hintText: '查找产品名称、质料、标准、特点',
border: InputBorder.none,
isDense: true,
isCollapsed: false,
),
autofocus: true,
style: TextStyleMs.ff_333333_16,
controller: state.controller,
onChanged: (value) {
//运用controller 判断
if (state.controller.value.isComposingRangeValid) {
return;
}
//退出软键盘
FocusScope.of(viewService.context)
.requestFocus(state.blankNode);
dispatch(
GoodsSearchActionCreator.onUpdateSearchText(
value));
dispatch(GoodsSearchActionCreator.onSearchFoods(
value));
},
),
),
实测: 该办法回来的isComposingRangeValid 针对ios判断依然是 true,没有作用。
正确解决思路
/// Builds [TextSpan] from current editing value.
///
/// By default makes text in composing range appear as underlined. Descendants
/// can override this method to customize appearance of text.
TextSpan buildTextSpan({TextStyle style , bool withComposing}) {
assert(!value.composing.isValid || !withComposing || value.isComposingRangeValid);
// If the composing range is out of range for the current text, ignore it to
// preserve the tree integrity, otherwise in release mode a RangeError will
// be thrown and this EditableText will be built with a broken subtree.
// 注释1
if (!value.isComposingRangeValid || !withComposing) {
return TextSpan(style: style, text: text);
}
final TextStyle composingStyle = style.merge(
const TextStyle(decoration: TextDecoration.underline),
);
// 注释2
return TextSpan(
style: style,
children: <TextSpan>[
TextSpan(text: value.composing.textBefore(value.text)),
TextSpan(
style: composingStyle,
text: value.composing.textInside(value.text),
),
TextSpan(text: value.composing.textAfter(value.text)),
]);
}
通过检查TextEditingController源码,看到源码已经对输入内容做了必定的判断
- 通过来判断 value.composing ,第一个判断是直接输入的,那就直接回来一个一般款式
- value.composing.textInside(value.text)获取到的就是输入未完结的字符,默许是添加了一个下划线的款式(composingStyle)。依据注释,google提示咱们可以重写此办法改写款式!
正确解决办法
新建一个controller继承自TextEditingController,重写buildTextSpan办法
class ChinaTextEditController extends TextEditingController{
///拼音输入完结后的文字
var completeText = '';
@override
TextSpan buildTextSpan({TextStyle style, bool withComposing}) {
///拼音输入完结
if (!value.composing.isValid || !withComposing) {
if(completeText!=value.text){
completeText = value.text;
WidgetsBinding.instance.addPostFrameCallback((_){
notifyListeners();
});
}
return TextSpan(style: style, text: text);
}
///回来输入款式,可自定义款式
final TextStyle composingStyle = style.merge(
const TextStyle(decoration: TextDecoration.underline),
);
return TextSpan(
style: style,
children: <TextSpan>[
TextSpan(text: value.composing.textBefore(value.text)),
TextSpan(
style: composingStyle,
text:
value.composing.isValid && !value.composing.isCollapsed?
value.composing.textInside(value.text):"",
),
TextSpan(text: value.composing.textAfter(value.text)),
]);
}
}
redux 的view 中运用
Expanded(
flex: 1,
child: TextField(
decoration: new InputDecoration(
hintText: '查找产品名称、质料、标准、特点',
border: InputBorder.none,
isDense: true,
isCollapsed: false,
),
autofocus: true,
style: TextStyleMs.ff_333333_16,
controller: state.controller,
),
),
redux state中绑定
class GoodsSearchState implements Cloneable<GoodsSearchState> {
...
ChinaTextEditController _controller;
ChinaTextEditController get controller => _controller;
set controller(TextEditingController value) {
_controller = value;
}
///空白焦点 用来躲藏软键盘
FocusNode blankNode;
@override
GoodsSearchState clone() {
return GoodsSearchState()
..searchText = searchText
.._controller = _controller
...
;
}
}
GoodsSearchState initState(Map<String, dynamic> args) {
GoodsSearchState state = GoodsSearchState();
state.searchText = '';
state.blankNode = FocusNode();
state.controller = ChinaTextEditController();
state.controller.text = state.searchText;
//输入下标在文字之后
state.controller.selection = TextSelection.fromPosition(TextPosition(
affinity: TextAffinity.downstream, offset: state.searchText.length));
...
return state;
}