Flutter基础组件(3):TextField(输入框)

 


TextField是一个允许用户输入文本的小部件。它非常灵活,支持多种文本输入场景,如单行文本、多行文本、密码输入、数值输入等。TextField还提供了丰富的定制选项,包括文本样式、图标、控制器等。

一、基础用法

TextField最基本的用法是创建一个可以输入单行文本的字段:

TextField(
  decoration: InputDecoration(
    border: OutlineInputBorder(),
    labelText: 'Enter your name',
  ),
)

这将创建一个带有标签和下划线的文本输入框。

二、常用属性

下面是一些常用的TextField属性以及它们的说明:

  • controller:控制输入框的文本内容,可以通过TextEditingController进行管理。
  • decoration:输入框的装饰,可以定义输入框的边框、背景、提示文本等样式。
    • icon:设置左边显示的图标
    • labelText:在输入框上面显示一个提示的文本
    • hintText:显示提示的占位文字
    • border:输入框的边框,默认底部有一个边框,可以通过 InputBorder.none 删除掉
    • filled:是否填充输入框,默认为false
    • fillColor:输入框填充的颜色
  • obscureText:是否将输入内容隐藏,常用于密码输入框。
  • enabled:输入框是否可编辑。
  • maxLength:允许输入的最大字符数。
  • textInputAction:键盘操作按钮的类型,例如完成、继续等。
  • keyboardType:键盘的类型,如文本、数字、URL等。
  • onChanged:文本变化时的回调函数。
  • onSubmitted:用户提交输入时的回调函数。
  • focusNode:用于控制输入框的焦点获取和失去。
  • autofocus:是否自动获取焦点。
  • style:输入框文本的样式,如字体大小、颜色等。

2.1 文本输入类型

TextField支持多种文本输入类型,通过TextInputType属性设置:


文本输入

TextField(
  keyboardType: TextInputType.text,
  // ... 其他属性
)

数字输入

TextField(
  keyboardType: TextInputType.number,
  // ... 其他属性
)

多行文本

TextField(
  keyboardType: TextInputType.multiline,
  maxLines: null, // 允许无限行
  // ... 其他属性
)

密码输入

TextField(
  obscureText: true, // 隐藏输入的文本
  // ... 其他属性
)

obscureText属性用于指定输入框的文本是否应该被隐藏,常用于密码输入框。当设置为true时,输入的文本将以圆点或其他隐藏字符显示。


2.2 控制器和焦点

TextField可以使用TextEditingController控制输入的文本,以及通过使用FocusNode监听焦点的改变事件:

TextEditingController _controller = TextEditingController(); // 创建控制器
FocusNode _focusNode = FocusNode(); // _focusNode绑定输入框

TextField(
  controller: _controller,
  focusNode: _focusNode,

  // 监听焦点变化    
  _focusNode.addListener((){
      print(focusNode.hasFocus);
  });  
)

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


2.3 输入装饰

InputDecoration是一个用于定义TextField外观的类,包括标签、占位符文本、前缀/后缀图标、错误文本等:

TextField(
  decoration: InputDecoration(
    prefixIcon: Icon(Icons.person),
    suffixIcon: Icon(Icons.clear),
    hintText: 'Enter your name',
    errorText: 'This field is required',
    contentPadding: EdgeInsets.all(8.0),
    border: OutlineInputBorder(),
  ),
  // ... 其他属性
)

2.4 输入验证

TextField支持输入验证,通过TextInputFormatter或者validator属性:

TextField(
  inputFormatters: [
    WhitelistingTextInputFormatter.digitsOnly, // 只允许输入数字
  ],
  validator: (value) {
    if (value == null || value.isEmpty) {
      return 'Please enter your name';
    }
    return null;
  },
  // ... 其他属性
)

2.5 自动完成和填充

TextField支持自动完成和自动填充功能,通过AutofillHints

TextField(
  autofillHints: [AutofillHints.name],
  // ... 其他属性
)

三、获取输入内容

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

  • 定义两个变量,用于保存用户名和密码,然后在onChange触发时,各自保存一下输入内容。
  • 通过controller直接获取。

第一种方式比较简单,不再举例,我们来重点看一下第二种方式,我们以用户名输入框举例:

定义一个controller

// 定义一个controller
TextEditingController _unameController = TextEditingController();

然后设置输入框 controller:

TextField(
    autofocus: true,
    controller: _unameController, // 设置controller
    // ...
)

通过 controller 获取输入框内容

debugPrint(_unameController.text)

四、监听文本输入完成和提交

你可以监听文本输入完成和提交的回调,例如,当用户输入文本时执行一些操作:

TextField(
  onEditingComplete: () {
    // 文本输入完成时的回调
  },
  onSubmitted: (value) {
    // 文本提交时的回调
  },
  // ... 其他属性
)

五、监听文本变化

监听文本变化也有两种方式:

  • 设置onChange回调,如:
TextField(
    autofocus: true,
    onChanged: (value) {
        print("onChange: $value");
    }
)
  • 通过controller监听,如:
@override
void initState() {
    // 监听输入改变  
    _unameController.addListener((){
        print(_unameController.text);
    });
}

两种方式相比,onChanged是专门用于监听文本变化,而controller的功能却多一些,除了能监听文本变化外,它还可以设置默认值、选择文本,下面我们看一个例子:

示例代码如下:

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(20),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[TextFieldDemo()],
      ),
    );
  }
}

class TextFieldDemo extends StatefulWidget {
  @override
  _TextFieldDemoState createState() => _TextFieldDemoState();
}

class _TextFieldDemoState extends State<TextFieldDemo> {
  @override
  Widget build(BuildContext context) {
    return TextField(
      decoration: InputDecoration(
          icon: Icon(Icons.people),
          labelText: "username",
          hintText: "请输入用户名",
          border: InputBorder.none,
          filled: true,
          fillColor: Colors.lightGreen),
      onChanged: (value) {
        print("onChanged:$value");
      },
      onSubmitted: (value) {
        print("onSubmitted:$value");
      },
    );
  }
}

运行效果如下图所示:

Flutter_edit_H.png


六、自定义样式

虽然我们可以通过decoration属性来定义输入框样式,下面以自定义输入框下划线颜色为例来介绍一下:

TextField(
    decoration: InputDecoration(
        labelText: "请输入用户名",
        prefixIcon: Icon(Icons.person),
        // 未获得焦点下划线设为灰色
        enabledBorder: UnderlineInputBorder(
            borderSide: BorderSide(color: Colors.grey),
        ),
        //获得焦点下划线设为蓝色
        focusedBorder: UnderlineInputBorder(
            borderSide: BorderSide(color: Colors.blue),
        ),
    ),
),

上面代码我们直接通过InputDecorationenabledBorderfocusedBorder来分别设置了输入框在未获取焦点和获得焦点后的下划线颜色。另外,我们也可以通过主题来自定义输入框的样式,下面我们探索一下如何在不使用enabledBorderfocusedBorder的情况下来自定义下滑线颜色。


由于TextField在绘制下划线时使用的颜色是主题色里面的hintColor,但提示文本颜色也是用的hintColor, 如果我们直接修改hintColor,那么下划线和提示文本的颜色都会变。值得高兴的是decoration中可以设置hintStyle,它可以覆盖hintColor,并且主题中可以通过inputDecorationTheme来设置输入框默认的decoration。所以我们可以通过主题来自定义,代码如下:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});    
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: MyHomeBody(),
      )
    );
  }
}

class MyHomeBody extends StatelessWidget {
  const MyHomeBody({super.key});  
  @override
  Widget build(BuildContext context) {
    return Theme(
        data: Theme.of(context).copyWith(
            hintColor: Colors.grey[200], //定义下划线颜色
            inputDecorationTheme: const InputDecorationTheme(
                labelStyle: TextStyle(color: Colors.grey), //定义label字体样式
                hintStyle:
                    TextStyle(color: Colors.grey, fontSize: 14.0) //定义提示文本样式
                )),
        child: const Column(
          children: <Widget>[
            TextField(
              decoration: InputDecoration(
                  labelText: "用户名",
                  hintText: "用户名或邮箱",
                  prefixIcon: Icon(Icons.person)),
            ),
            TextField(
              decoration: InputDecoration(
                  prefixIcon: Icon(Icons.lock),
                  labelText: "密码",
                  hintText: "您的登录密码",
                  hintStyle: TextStyle(color: Colors.grey, fontSize: 13.0)),
              obscureText: true,
            )
          ],
        ));
  }
}

运行效果如下图所示:

Flutter_edit_J.png


我们成功的自定义了下划线颜色和提问文字样式,细心的读者可能已经发现,通过这种方式自定义后,输入框在获取焦点时,labelText不会高亮显示了,正如上图中的"用户名"本应该显示蓝色,但现在却显示为灰色,并且我们还是无法定义下划线宽度。另一种灵活的方式是直接隐藏掉TextField本身的下划线,然后通过Container去嵌套定义样式,如:

Container(
    child: TextField(
        keyboardType: TextInputType.emailAddress,
        decoration: InputDecoration(
            labelText: "Email",
            hintText: "电子邮件地址",
            prefixIcon: Icon(Icons.email),
            border: InputBorder.none //隐藏下划线
        )
    ),
    decoration: BoxDecoration(
        // 下滑线浅灰色,宽度1像素
        border: Border(bottom: BorderSide(color: Colors.grey[200], width: 1.0))
    ),
)

运行效果如下图:

Flutter_edit_F.png


通过这种组件组合的方式,也可以定义背景圆角等。一般来说,优先通过decoration来自定义样式,如果decoration实现不了,再用widget组合的方式。


八、示例

8.1 示例1:登录输入框

我们实现一个登录表单:

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        TextField(
          autofocus: true,
          decoration: InputDecoration(
              labelText: "用户名",
              hintText: "用户名或邮箱",
              prefixIcon: Icon(Icons.person)),
        ),
        TextField(
          decoration: InputDecoration(
              labelText: "密码",
              hintText: "您的登录密码",
              prefixIcon: Icon(Icons.lock)),
          obscureText: true,
        ),
      ],
    );
  }
}

运行后,效果如下图所示:

Flutter_edit_I.png


8.2 示例2:完整的登录输入框

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    home: CustomTextFieldPage(),
  ));
}

class CustomTextFieldPage extends StatefulWidget {
  const CustomTextFieldPage({super.key});
  @override
  State<CustomTextFieldPage> createState() => _CustomTextFieldPageState();
}

class _CustomTextFieldPageState extends State<CustomTextFieldPage> {
  // 创建焦点节点对象
  final FocusNode _nameFocusNode = FocusNode();
  final FocusNode _passwordFocusNode = FocusNode();
  // 记录焦点状态
  bool _nameHasFocus = false;
  bool _passwordHasFocus = false;

  @override
  void initState() {
    super.initState();
    // 监听焦点变化
    _nameFocusNode.addListener(_handleNameFocusChange);
    _passwordFocusNode.addListener(_handlePasswordFocusChange);
  }

  @override
  void dispose() {
    // 移除焦点监听
    _nameFocusNode.removeListener(_handleNameFocusChange);
    _passwordFocusNode.removeListener(_handlePasswordFocusChange);
    _nameFocusNode.dispose();
    _passwordFocusNode.dispose();
    super.dispose();
  }

  void _handleNameFocusChange() {
    // 处理姓名输入框焦点变化
    setState(() {
      _nameHasFocus = _nameFocusNode.hasFocus;
    });
  }

  void _handlePasswordFocusChange() {
    // 处理密码输入框焦点变化
    setState(() {
      _passwordHasFocus = _passwordFocusNode.hasFocus;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('自定义输入框'),
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text(
                '请输入您的姓名',
                style: TextStyle(fontSize: 18.0),
              ),
              const SizedBox(height: 16.0),
              TextField(
                focusNode: _nameFocusNode,
                decoration: InputDecoration(
                  filled: true,
                  fillColor:
                      _nameHasFocus ? Colors.lightBlue[100] : Colors.grey[200],
                  border: const OutlineInputBorder(),
                  hintText: '请输入姓名',
                  hintStyle: const TextStyle(color: Colors.grey),
                  prefixIcon: const Icon(Icons.person),
                ),
              ),
              const SizedBox(height: 16.0),
              TextField(
                focusNode: _passwordFocusNode,
                obscureText: true,
                decoration: InputDecoration(
                  filled: true,
                  fillColor: _passwordHasFocus
                      ? Colors.lightBlue[100]
                      : Colors.grey[200],
                  border: const OutlineInputBorder(),
                  hintText: '请输入密码',
                  hintStyle: const TextStyle(color: Colors.grey),
                  prefixIcon: const Icon(Icons.lock),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

运行后,效果如下图所示:

Flutter_edit_K.png


代码解析:

  • 这段代码的功能是展示一个包含两个自定义样式输入框的页面,并在输入框获得焦点和失去焦点时改变其样式。

  • 在这段代码中,我们使用了 FocusNode 来监听输入框的焦点状态。FocusNode 是一个用于管理焦点的类,在输入框获得或失去焦点时会触发相应的回调函数。

  • 具体来说,代码中的 _nameFocusNode_passwordFocusNode 是两个 FocusNode 实例,分别用于管理姓名输入框和密码输入框的焦点。

  • 通过在 initState() 方法中为每个 FocusNode 添加监听器(addListener),我们能够在焦点状态发生变化时得到通知。具体来说,我们为 _nameFocusNode 添加了 _handleNameFocusChange 回调函数,为 _passwordFocusNode 添加了 _handlePasswordFocusChange 回调函数。

  • 当输入框获得焦点时,_handleNameFocusChange_handlePasswordFocusChange 回调函数会被触发。在这些回调函数中,我们使用 setState() 方法来更新对应的 _nameHasFocus_passwordHasFocus 变量,以记录焦点的状态。

  • build() 方法中,我们根据 _nameHasFocus_passwordHasFocus 变量的值来决定输入框的背景颜色。当输入框获得焦点时,背景颜色设置为浅蓝色(Colors.lightBlue[100]),当输入框失去焦点时,背景颜色设置为灰色(Colors.grey[200])。

  • 通过这种方式,我们能够根据输入框的焦点状态动态地改变其样式,提供更直观的用户反馈。当用户点击输入框时,输入框会获得焦点,背景颜色变为浅蓝色;当用户点击其他区域时,输入框失去焦点,背景颜色恢复为灰色。


posted @   fengMisaka  阅读(432)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示