Flutter Widget(4):输入框和表单

1.TextField

文本输入框,相当于Android中的EditText

  • controller:编辑框的控制器,通过它可以设置/获取编辑框的内容、选择编辑内容、监听编辑文本改变事件。大多数情况下我们都需要显式提供一个controller来与文本框交互。如果没有提供controller,则TextField内部会自动创建一个。
  • focusNode:用于控制TextField是否占有当前键盘的输入焦点。它是我们和键盘交互的一个handle。
  • InputDecoration:用于控制TextField的外观显示,如提示文本、背景颜色、边框等。
  • keyboardType:用于设置该输入框默认的键盘输入类型,取值如下:
    • TextInputType.text :文本输入键盘
    • TextInputType.multiline :多行文本,需和maxLines配合使用(设为null或大于1)
    • TextInputType.number :数字;会弹出数字键盘
    • TextInputType.phone :优化后的电话号码输入键盘;会弹出数字键盘并显示"* #"
    • TextInputType.datetime :优化后的日期输入键盘;Android上会显示“: -”
    • TextInputType.emailAddress :优化后的电子邮件地址;会显示“@ .”
    • TextInputType.url:优化后的url输入键盘; 会显示“/ .”
  • textInputAction:键盘动作按钮图标(即回车键位图标),它是一个枚举值,有多个可选值
  • style:正在编辑的文本样式。
  • textAlign: 输入框内编辑文本在水平方向的对齐方式。
  • autofocus: 是否自动获取焦点。
  • obscureText:是否隐藏正在编辑的文本,如用于输入密码的场景等,文本内容会用“•”替换。
  • maxLines:输入框的最大行数,默认为1;如果为null,则无行数限制。
  • maxLength和maxLengthEnforced :maxLength代表输入框文本的最大长度,设置后输入框右下角会显示输入的文本计数。maxLengthEnforced决定当输入文本长度超过maxLength时是否阻止输入,为true时会阻止输入,为false时不会阻止输入但输入框会变红。
  • onChange:输入框内容改变时的回调函数;注:内容改变事件也可以通过controller来监听。
  • onEditingComplete和onSubmitted:这两个回调都是在输入框输入完成时触发,比如按了键盘的完成键(对号图标)或搜索键(?图标)。不同的是两个回调签名不同,onSubmitted回调是ValueChanged<String>类型,它接收当前输入内容做为参数,而onEditingComplete不接收参数。
  • inputFormatters:用于指定输入格式;当用户输入内容改变时,会根据指定的格式来校验。
  • enable:如果为false,则输入框会被禁用,禁用状态不接收输入和事件,同时显示禁用态样式(在其decoration中定义)。
  • cursorWidth、cursorRadius和cursorColor:这三个属性是用于自定义输入框光标宽度、圆角和颜色的。

获取输入内容
1.通过onChange获取

  TextField(
              keyboardType: TextInputType.text,
              autofocus: true,
              decoration: InputDecoration(
                  hintText: "用户名或邮箱",
                  prefixIcon: Icon(Icons.person),
              ),
              onChanged: (s){
                print(s);
              },
              controller:_unameController,
            )

2.通过controller直接获取

//定义一个controller
TextEditingController _unameController=new TextEditingController();
//然后设置输入框controller:
TextField(
    autofocus: true,
    controller: _unameController, //设置controller
    ...
)
//通过controller获取输入框内容
print(_unameController.text)

监听文本变化
1.设置onChange回调,如:

TextField(
    autofocus: true,
    onChanged: (v) {
      print("onChange: $v");
    }
)

2.通过controller监听,如:

@override
void initState() {
  //监听输入改变  
  _unameController.addListener((){
    print(_unameController.text);
  });
}

控制焦点

焦点可以通过FocusNode和FocusScopeNode来控制,默认情况下,焦点由FocusScope来管理,它代表焦点控制范围,可以在这个范围内可以通过FocusScopeNode在输入框之间移动焦点、设置默认焦点等。我们可以通过FocusScope.of(context) 来获取widget树中默认的FocusScopeNode。

焦点切换

class FocusTestRoute extends StatefulWidget {
  @override
  _FocusTestRouteState createState() => new _FocusTestRouteState();
}

class _FocusTestRouteState extends State<FocusTestRoute> {
  FocusNode focusNode1 = new FocusNode();
  FocusNode focusNode2 = new FocusNode();
  FocusScopeNode focusScopeNode;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(16.0),
      child: Column(
        children: <Widget>[
          TextField(
            autofocus: true, 
            focusNode: focusNode1,//关联focusNode1
            decoration: InputDecoration(
                labelText: "input1"
            ),
          ),
          TextField(
            focusNode: focusNode2,//关联focusNode2
            decoration: InputDecoration(
                labelText: "input2"
            ),
          ),
          Builder(builder: (ctx) {
            return Column(
              children: <Widget>[
                RaisedButton(
                  child: Text("移动焦点"),
                  onPressed: () {
                    //将焦点从第一个TextField移到第二个TextField
                    // 这是一种写法 FocusScope.of(context).requestFocus(focusNode2);
                    // 这是第二种写法
                    if(null == focusScopeNode){
                      focusScopeNode = FocusScope.of(context);
                    }
                    focusScopeNode.requestFocus(focusNode2);
                  },
                ),
                RaisedButton(
                  child: Text("隐藏键盘"),
                  onPressed: () {
                    // 当所有编辑框都失去焦点时键盘就会收起  
                    focusNode1.unfocus();
                    focusNode2.unfocus();
                  },
                ),
              ],
            );
          },
          ),
        ],
      ),
    );
  }
}

监听焦点状态改变事件

// 创建 focusNode   
FocusNode focusNode = new FocusNode();
...
// focusNode绑定输入框   
TextField(focusNode: focusNode);
...
// 监听焦点变化    
focusNode.addListener((){
   print(focusNode.hasFocus);
});

获得焦点时focusNode.hasFocus值为true,失去焦点时为false。

2.Form

可以对输入框进行分组,然后进行一些统一操作,如输入内容校验、输入框重置以及输入内容保存。

Form({
  @required Widget child,
  bool autovalidate = false,
  WillPopCallback onWillPop,
  VoidCallback onChanged,
})
  • autovalidate:是否自动校验输入内容;当为true时,每一个子FormField内容发生变化时都会自动校验合法性,并直接显示错误信息。否则,需要通过调用FormState.validate()来手动校验。
  • onWillPop:决定Form所在的路由是否可以直接返回(如点击返回按钮),该回调返回一个Future对象,如果Future的最终结果是false,则当前路由不会返回;如果为true,则会返回到上一个路由。此属性通常用于拦截返回按钮。
  • onChanged:Form的任意一个子FormField内容发生变化时会触发此回调。
FormField

Form的子孙元素必须是FormField类型,FormField是一个抽象类,Flutter提供了一个TextFormField widget,它继承自FormField类,也是TextField的一个包装类,所以除了FormField定义的属性之外,它还包括TextField的属性。

const FormField({
  ...
  FormFieldSetter<T> onSaved, //保存回调
  FormFieldValidator<T>  validator, //验证回调
  T initialValue, //初始值
  bool autovalidate = false, //是否自动校验。
})
FormState

FormState为Form的State类,可以通过Form.of()或GlobalKey获得。我们可以通过它来对Form的子孙FormField进行统一操作。我们看看其常用的三个方法:

  • FormState.validate():调用此方法后,会调用Form子孙FormField的validate回调,如果有一个校验失败,则返回false,所有校验失败项都会返回用户返回的错误提示。
  • FormState.save():调用此方法后,会调用Form子孙FormField的save回调,用于保存表单内容
  • FormState.reset():调用此方法后,会将子孙FormField的内容清空。

例子

class LoginPage extends StatefulWidget {
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  GlobalKey _formKey= new GlobalKey<FormState>();
  TextEditingController _name=TextEditingController();
  TextEditingController _pwd=TextEditingController();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(),
          body: Container(
            padding: EdgeInsets.all(20.0),
            child: Form(
              autovalidate: true,
                key: _formKey,
                child: Column(
              children: <Widget>[
                TextFormField(
                  decoration: InputDecoration(
                      hintText: '请输入用户名',
                      border: OutlineInputBorder(
                        borderSide: BorderSide(
                          color: Colors.grey,
                          style: BorderStyle.solid,
                          width: 0.0,
                        ),
                        borderRadius: BorderRadius.all(Radius.circular(30.0)),
                      )),
                  validator: (value) {
                    return value.length == 3 ? null : '账号不存在';
                  },
                  controller: _name,
                ),
                SizedBox(
                  height: 15.0,
                ),
                TextFormField(
                  decoration: InputDecoration(
                      hintText: '请输入密码',
                      border: OutlineInputBorder(
                        borderSide: BorderSide(
                          color: Colors.grey,
                          style: BorderStyle.solid,
                          width: 5.0,
                        ),
                        borderRadius: BorderRadius.all(Radius.circular(10.0)),
                      )),
                  validator: (value) {
                    return value.length < 6 ? '密码不能小于6位' : null;
                  },
                  controller: _pwd,
                ),
                RaisedButton(
                  onPressed:(){
                    _login();
                  } ,
                  child: Text(
                    '登錄',
                  ),
                ),
              ],
            )),
          )),
    );
  }

   void _login() {
    FormState _loginForm=_formKey.currentState;
    if(_loginForm.validate()){
        print('name:'+_name.text);
        print('pwd:'+_pwd.text);
    }
  }
}
发布了82 篇原创文章 · 获赞 155 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/hxl517116279/article/details/89067274