← Frontend / Flutter

06_表单与输入

Flutter 表单组件和输入处理

TextField、Form、表单验证、Checkbox、Switch 等输入控件。


TextField(输入框)

// 基础输入框
TextField(
  decoration: const InputDecoration(
    labelText: '用户名',              // 浮动标签
    hintText: '请输入用户名',          // 占位提示
    prefixIcon: Icon(Icons.person),  // 前缀图标
    suffixIcon: Icon(Icons.clear),   // 后缀图标
    border: OutlineInputBorder(),    // 边框样式
    filled: true,                    // 背景填充
    fillColor: Colors.grey,
  ),
  keyboardType: TextInputType.emailAddress, // 键盘类型
  obscureText: true,                        // 密码模式
  maxLength: 50,                            // 最大字符数
  maxLines: 3,                              // 多行输入(类似 textarea)
  onChanged: (value) => print(value),       // 值变化回调
  onSubmitted: (value) => submit(value),    // 提交回调(按键盘完成键)
)

keyboardType 常用值:

值 用途
TextInputType.text 普通文字(默认)
TextInputType.emailAddress 邮箱(@ 键更明显)
TextInputType.number 纯数字
TextInputType.phone 电话号码
TextInputType.multiline 多行文本
TextInputType.url URL

TextEditingController(读写输入值)

类似 Vue 的 ref() / React 的 useRef():

class _MyFormState extends State<MyForm> {
  final _nameController = TextEditingController();
  final _emailController = TextEditingController();

  @override
  void initState() {
    super.initState();
    // 设置初始值
    _nameController.text = '默认用户名';
  }

  @override
  void dispose() {
    // 必须释放,否则内存泄漏!
    _nameController.dispose();
    _emailController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(controller: _nameController),
        TextField(controller: _emailController),
        ElevatedButton(
          onPressed: () {
            print(_nameController.text);     // 读取值
            _nameController.text = '新名称'; // 设置值
            _nameController.clear();         // 清空
            // 设置光标位置
            _nameController.selection = TextSelection.fromPosition(
              TextPosition(offset: _nameController.text.length),
            );
          },
          child: const Text('操作'),
        ),
      ],
    );
  }
}

Form 与 TextFormField(表单验证)

class LoginForm extends StatefulWidget {
  const LoginForm({super.key});

  @override
  State<LoginForm> createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
  final _formKey = GlobalKey<FormState>(); // 表单唯一标识(用于触发验证)
  final _emailController = TextEditingController();
  final _passwordController = TextEditingController();
  bool _obscurePassword = true;

  @override
  void dispose() {
    _emailController.dispose();
    _passwordController.dispose();
    super.dispose();
  }

  void _submit() {
    if (_formKey.currentState!.validate()) { // 触发所有字段验证
      print('提交: ${_emailController.text}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          // TextFormField = TextField + 验证功能
          TextFormField(
            controller: _emailController,
            decoration: const InputDecoration(
              labelText: '邮箱',
              prefixIcon: Icon(Icons.email),
              border: OutlineInputBorder(),
            ),
            keyboardType: TextInputType.emailAddress,
            validator: (value) {             // 返回 null = 验证通过;返回字符串 = 错误提示
              if (value == null || value.isEmpty) return '邮箱不能为空';
              final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
              if (!emailRegex.hasMatch(value)) return '邮箱格式不正确';
              return null;
            },
          ),
          const SizedBox(height: 16),
          TextFormField(
            controller: _passwordController,
            decoration: InputDecoration(
              labelText: '密码',
              prefixIcon: const Icon(Icons.lock),
              border: const OutlineInputBorder(),
              suffixIcon: IconButton(
                icon: Icon(_obscurePassword ? Icons.visibility_off : Icons.visibility),
                onPressed: () => setState(() => _obscurePassword = !_obscurePassword),
              ),
            ),
            obscureText: _obscurePassword,
            validator: (value) {
              if (value == null || value.isEmpty) return '密码不能为空';
              if (value.length < 6) return '密码至少 6 位';
              return null;
            },
          ),
          const SizedBox(height: 24),
          SizedBox(
            width: double.infinity,          // 宽度撑满(类似 width: 100%)
            height: 48,
            child: ElevatedButton(
              onPressed: _submit,
              child: const Text('登录', style: TextStyle(fontSize: 16)),
            ),
          ),
        ],
      ),
    );
  }
}

其他输入控件

Checkbox

class _CheckboxState extends State<CheckboxDemo> {
  bool _isChecked = false;

  @override
  Widget build(BuildContext context) {
    return CheckboxListTile(          // 带 label 的 Checkbox(更常用)
      title: const Text('记住密码'),
      value: _isChecked,
      onChanged: (value) => setState(() => _isChecked = value!),
    );
  }
}

Switch

SwitchListTile(
  title: const Text('开启通知'),
  subtitle: const Text('接收推送消息'),
  value: _notificationEnabled,
  onChanged: (value) => setState(() => _notificationEnabled = value),
)

Radio(单选)

Column(
  children: ['男', '女', '保密'].map((option) =>
    RadioListTile<String>(
      title: Text(option),
      value: option,
      groupValue: _selectedGender,
      onChanged: (value) => setState(() => _selectedGender = value),
    ),
  ).toList(),
)

Slider(滑块)

Slider(
  value: _volume,
  min: 0,
  max: 100,
  divisions: 10,               // 刻度数
  label: '${_volume.round()}%',
  onChanged: (value) => setState(() => _volume = value),
)

DropdownButton(下拉选择)

DropdownButtonFormField<String>(
  value: _selectedCity,
  decoration: const InputDecoration(
    labelText: '城市',
    border: OutlineInputBorder(),
  ),
  items: ['北京', '上海', '广州', '深圳'].map((city) =>
    DropdownMenuItem(value: city, child: Text(city)),
  ).toList(),
  onChanged: (value) => setState(() => _selectedCity = value),
  validator: (value) => value == null ? '请选择城市' : null,
)