【Flutter】深入剖析TextField组件

属性值

TextField组件本身具备多种属性,支持很多参数设置来实现不同样式效果。 TextField组件可直接上手使用,但默认样式和输入规则并不一定是需求开发中想要的(实话说默认样式并不好看)。下面就通过TextField组件属性介绍来自定义属于自己的输入框吧。

TextField(); 

基础功能

  • obscureText: true表示隐藏输入内容,类似密码输入
  • readOnly: true表示输入框禁止输入
  • textCapitalization:控制输入内容大小写(words 首字母大写、sentences 句子首字母大写、characters 所有字母大写),可能存在兼容性问题会不生效。
  • minLines & maxLines: 最小行数和最大行数设置,若只设置最大行数输入框高度就是maxLines的高度,否则是minLines的高度。
  • maxLength: 输入字符限制器。在输入框下方会出现输入字符数量(一般也不希望透出计数)

样式

样式设置主要未然光标和输入框装饰内容设置。

光标

光标支持自定义,支持设置颜色、高度、宽度、圆角以及是否展示光标。

cursorColor:  Colors.black,
cursorHeight:  10,
cursorWidth: 5,
cursorRadius: Radius.circular(10),
showCursor: false, 
装饰

前面提到输入框默认样式比较普通,日常开发中需要设置输入框样式。自定义输入框样式是可以通过decoration来实现的。

  • 外框

外框可以使用内置官方已有样式,也能通过自定义边框来实现。

decoration: InputDecoration(
  border: OutlineInputBorder()
) 
  • hint提示

默认未输入内容的提示信息

decoration: InputDecoration(
  hintText: "请输入"
) 
  • 悬浮提示

悬浮提示就是当获取焦点后的输入信息的提示(不太常用)

decoration: InputDecoration(
  lableText: "请输入"
) 

另外InputDecoration还支持设置其他border,例如错误状态、获取交点状态、不可用状态等都能自定义。

软键盘

  • textInputAction: 软键盘按钮样式和功能设置(none、unspecified、done、go、search、next等)实现软键盘按钮不同操作能力。
  • focusNode: 控制焦点使用,通过它实现弹起或收起软键盘能力。
  • autofocus: 是否主动获取焦点,true时在初始化后会自动获取焦点并弹起软键盘(一般情况下是会弹起,特殊情况除外)

输入控制

获取输入内容的方式有两种:onChangedcontroller

  • onChanged
TextField(
    onChanged:(text){
        // 这个可以返回输入文本
    }
) 
  • controller
TextEditingController controller = TextEditingController();
controller.addListener((){
    // 这个可以返回输入文本
    controller.text;
});
TextField(
    controller: controller,
) 
  • inputFormatters: 输入字符限制,通过自定义限制输入内容。

这里举例内置的LengthLimitingTextInputFormatter用来限制输入字符数。这和基础功能中maxLength是一样的作用,不同点在于maxLength会携带计数器。

源码解析

TextField继承StatefulWidget,所以就直接来看_TextFieldState

_TextFieldState

_TextFieldState中的build方法中实现输入框的展示。在内部多层组件嵌套下(FocusTrapAreaMouseRegion等)核心是EditableText组件。 另外在build方法中还有switch (theme.platform)方法,由于各个平台中对于输入框的实现存在差异,例如安卓和iOS输入框的光标以及选择状态样式存在差异。因此这里的switch (theme.platform)主要就是对于平台差异性做一些设置,代码可见主要是光标和textSelectionControls文本选择上有所不同。

switch (theme.platform) {
  case TargetPlatform.iOS:
    final CupertinoThemeData cupertinoTheme = CupertinoTheme.of(context);
    forcePressEnabled = true;
    textSelectionControls ??= cupertinoTextSelectionControls;
    paintCursorAboveText = true;
    cursorOpacityAnimates = true;
    cursorColor ??= selectionTheme.cursorColor ?? cupertinoTheme.primaryColor;
    selectionColor = selectionTheme.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40);
    cursorRadius ??= const Radius.circular(2.0);
    cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0);
    autocorrectionTextRectColor = selectionColor;
    break;
    ......
  case TargetPlatform.android:
  case TargetPlatform.fuchsia:
    forcePressEnabled = false;
    textSelectionControls ??= materialTextSelectionControls;
    paintCursorAboveText = false;
    cursorOpacityAnimates = false;
    cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary;
    selectionColor = selectionTheme.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
    break; 

EditableTextState

_TextFieldState中平台差异性判断之后,接下来就是EditableText组件内部。同样EditableText是继承自StatefulWidget,略过之后直接看EditableTextState。在build方法中同样是通过多层嵌套(MouseRegionScrollableCompositedTransformaTarget)去看更主要的_Editable

_Editable

_Editable是继承自MultiChildRenderObjectWidget,绝大多数布局控件都是继承MultiChildRenderObjectWidget实现的。_Editable实现的渲染对象是RenderEditable

RenderEditable

RenderEditable就是RenderBox,就到了底层布局的测量、绘制、更新的过程。源码分析就直接看paint方法实现。

_paintContents部分是绘制文本内容,内部由TextPainter对文本绘制的实现。

@override
void paint(PaintingContext context, Offset offset) {
  _computeTextMetricsIfNeeded();
  if (_hasVisualOverflow && clipBehavior != Clip.none) {
    _clipRectLayer.layer = context.pushClipRect(
      needsCompositing,
      offset,
      Offset.zero & size,
      _paintContents,
      clipBehavior: clipBehavior,
      oldLayer: _clipRectLayer.layer,
    );
  } else {
    _clipRectLayer.layer = null;
    _paintContents(context, offset);
  }
  _paintHandleLayers(context, getEndpointsForSelection(selection!));
} 

_paintHandleLayers是文本选择器的绘制,若endpoints == 2则是段落选择。否则只是单选就只有一个光标选择。

void _paintHandleLayers(PaintingContext context, List<TextSelectionPoint> endpoints) {
  Offset startPoint = endpoints[0].point;
  startPoint = Offset(
    startPoint.dx.clamp(0.0, size.width),
    startPoint.dy.clamp(0.0, size.height),
  );
  context.pushLayer(
    LeaderLayer(link: startHandleLayerLink, offset: startPoint),
    super.paint,
    Offset.zero,
  );
  if (endpoints.length == 2) {
    Offset endPoint = endpoints[1].point;
    endPoint = Offset(
      endPoint.dx.clamp(0.0, size.width),
      endPoint.dy.clamp(0.0, size.height),
    );
    context.pushLayer(
      LeaderLayer(link: endHandleLayerLink, offset: endPoint),
      super.paint,
      Offset.zero,
    );
  }
} 

文末

我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。

需要的直接点击文末小卡片可以领取哦我免费分享给你,以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持,需要的自己领取)

Android学习PDF+架构视频+面试文档+源码笔记

部分资料一览:

  • 330页PDF Android学习核心笔记(内含8大板块)

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

  • Android BAT大厂面试题(有解析)

领取地址:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值