一、TextField 实现各种各样的输入框

TextField 是一个非常简单的表单输入框 Widget,基于 EditableText Widget 实现

支持的属性非常多:

  const TextField({
    Key key,
    this.controller,
    this.focusNode,
    this.decoration = const InputDecoration(),
    TextInputType keyboardType,
    this.textInputAction,
    this.textCapitalization = TextCapitalization.none,
    this.style,
    this.strutStyle,
    this.textAlign = TextAlign.start,
    this.textAlignVertical,
    this.textDirection,
    this.readOnly = false,
    this.showCursor,
    this.autofocus = false,
    this.obscureText = false,
    this.autocorrect = true,
    this.maxLines = 1,
    this.minLines,
    this.expands = false,
    this.maxLength,
    this.maxLengthEnforced = true,
    this.onChanged,
    this.onEditingComplete,
    this.onSubmitted,
    this.inputFormatters,
    this.enabled,
    this.cursorWidth = 2.0,
    this.cursorRadius,
    this.cursorColor,
    this.keyboardAppearance,
    this.scrollPadding = const EdgeInsets.all(20.0),
    this.dragStartBehavior = DragStartBehavior.start,
    this.enableInteractiveSelection,
    this.onTap,
    this.buildCounter,
    this.scrollController,
    this.scrollPhysics,
  })

支持单行和多行的文件输入及表单值的获取

1、普通表单

直接通过 TextField 可以显示一个最简单的表单:

TextField()

效果:

86566-69wwqzrcgac.png

2、边框表单、label 动效

如果是 Material Design 的 UI 设计风格,我们经常会看到边框的表单,点击之后,一个 placeholder 浮动到上面去

在 TextField 中通过配置 decoration 属性,传入一个 InputeDecoration Widget 来配置具体的显示内容, InputeDecoration 支持的属性也非常的多,这里就不举出了。

labelText 是可以浮动的提示文案,而 placeholder 其实是通过 hintText 指定的

TextField(
  decoration: InputDecoration(
    labelText: '表单label',
    labelStyle: TextStyle(
      color: Colors.pink,
      fontSize: 12,
    ),
    helperText: 'helperText',
    hintText: 'Placeholder...',
    border: OutlineInputBorder(
      borderSide: BorderSide(
        color: Colors.pink,
      ),
    ),
  ),
)

效果:

1.gif

上面我并没有配置 focus 状态的外边框的颜色,如果要指定,则可以通过 focusedBorder 指定:

focusedBorder: OutlineInputBorder(
  borderSide: BorderSide(
    color: Colors.pink,
  ),
)

效果:

47596-2oicvevt4mu.png

3、多行表单

TextField(
  maxLines: 4,
  decoration: InputDecoration(hintText: '多行文本'),
),

效果:

98720-rn1s8g8y1te.png

4、密码框

如果要使一个表单变成密码框,只需配置 obscureText 属性是 true

TextField(
  obscureText: true,
  decoration: InputDecoration(hintText: '密码框'),
),

75931-ea4l8x06cmr.png

5、icon 表单

InputDecoration 支持传入一个 icon 属性,传入之后,会在表单的前面显示一个 icon

TextField(
  obscureText: true,
  decoration: InputDecoration(
    hintText: '图标',
    icon: Icon(Icons.palette),
  ),

效果:

77645-2b2ox8rpdxb.png

二、TextField 获取表单值和设置初始值

如果要获取表单值和设置初始值,首先我们会使用一个 StatefulWidget,然后会监听 TextField 的 onChanged 事件,每次存取值。

这个逻辑在现代 Web 框架中已经很成熟了,但是初始值的设置以及表单值得绑定则不太一样。

TextField 接收一个 controller 属性,类型是 TextEditingController,从注释中可以看出,我们可以通过 controller 从外部控制表单的一切

类似于 Web 中拿到表单实例,然后在去控制表单一样

  /// Controls the text being edited.
  ///
  /// If null, this widget will create its own [TextEditingController].
  final TextEditingController controller;

1、设置初始值

首先我们需要定义一个 TextEditingController:

  TextEditingController _name = TextEditingController();

在 initState 的时候,设置初始值:

  @override
  void initState() {
    super.initState();
    this._name.text = 'ptbird'; // 设置初始值
  }

_name 传入给 TextField 组件:

TextField(
  decoration: InputDecoration(
    hintText: '账户',
    hintStyle: TextStyle(
      fontSize: 12,
      color: Colors.grey[300],
    ),
  ),
  controller: this._name,
  onChanged: (value) {
    this.setState(() {
      this._name.text = value;
    });
  },
)

先不关心 onChanged 的事件处理,效果如下:

65110-b07u0ac3w4n.png

2、获取表单值,并同步渲染到页面

因为没有双向绑定,和 React 处理思想类似,都是手动 setState

  onChanged: (value) {
    this.setState(() {
      this._pass.text = value;
    });
  },

在 Container 中渲染两个值:

          Container(
            width: double.infinity,
            child: Text('${this._name.text} - ${this._pass.text}'),
          ),

最终效果:

2.gif

3、完整 TextField 设置和控制代码

class _TextFieldDemoState extends State<TextFieldDemo> {
  TextEditingController _name = TextEditingController();
  TextEditingController _pass = TextEditingController();

  @override
  void initState() {
    super.initState();
    this._name.text = 'ptbird'; // 设置初始值
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(10),
      child: Column(
        children: <Widget>[
          TextField(
            decoration: InputDecoration(
              hintText: '账户',
              hintStyle: TextStyle(
                fontSize: 12,
                color: Colors.grey[300],
              ),
            ),
            controller: this._name,
            onChanged: (value) {
              this.setState(() {
                this._name.text = value;
              });
            },
          ),
          SizedBox(height: 10),
          TextField(
            obscureText: true,
            decoration: InputDecoration(
              hintText: '密码',
              hintStyle: TextStyle(
                fontSize: 12,
                color: Colors.grey[300],
              ),
            ),
            controller: this._pass,
            onChanged: (value) {
              this.setState(() {
                this._pass.text = value;
              });
            },
          ),
          SizedBox(height: 10),
          Container(
            width: double.infinity,
            height: 40,
            child: RaisedButton(
              child: Text('Login'),
              onPressed: () {
                print(this._name);
                print(this._pass);
              },
            ),
          ),
          SizedBox(height: 10),
          Container(
            width: double.infinity,
            child: Text('${this._name.text} - ${this._pass.text}'),
          ),
        ],
      ),
    );
  }
}

三、完整代码示例

1、TextField 各种样式

https://github.com/postbird/FlutterHelloWorldDemo/blob/master/demo1/lib/bak/main.38-%E8%A1%A8%E5%8D%95%E6%A0%B7%E5%BC%8F.dart

2、设置表单初始值和获取表单值

https://github.com/postbird/FlutterHelloWorldDemo/blob/master/demo1/lib/bak/main.39-%E8%8E%B7%E5%8F%96%E8%AE%BE%E7%BD%AE%E8%A1%A8%E5%8D%95%E5%80%BC.dart