输入组件详解

输入组件是与用户交互的核心,包括按钮、文本输入、选择器等。本教程将详细介绍 Flutter 中最常用的输入组件。

重要提示:Flutter 3.27+ 增强了 Material 3 组件的功能,提供了更多自定义选项和更好的用户体验。

1. TextField 文本输入框

TextField 是最常用的文本输入组件,支持单行和多行输入、自动验证、格式化等功能。

核心属性

属性 类型 说明
controller TextEditingController 控制器,用于访问和修改文本
decoration InputDecoration 装饰,设置外观和标签
keyboardType TextInputType 键盘类型
textInputAction TextInputAction 键盘动作按钮
obscureText bool 是否隐藏文本(密码输入)
maxLength int 最大长度
maxLines int 最大行数
enabled bool 是否启用
readOnly bool 是否只读

使用示例

// 基本 TextField
TextField(
  decoration: const InputDecoration(
    labelText: '用户名',
    hintText: '请输入用户名',
    border: OutlineInputBorder(),
  ),
)

// 带控制器的 TextField
final TextEditingController _controller = TextEditingController();

TextField(
  controller: _controller,
  decoration: const InputDecoration(
    labelText: '用户名',
    prefixIcon: Icon(Icons.person),
    suffixIcon: Icon(Icons.check),
  ),
  onChanged: (value) {
    print('输入: $value');
  },
  onSubmitted: (value) {
    print('提交: $value');
  },
)

// 密码输入框
TextField(
  obscureText: true,
  decoration: const InputDecoration(
    labelText: '密码',
    hintText: '请输入密码',
    border: OutlineInputBorder(),
    prefixIcon: Icon(Icons.lock),
  ),
)

// 多行文本输入
TextField(
  maxLines: 5,
  decoration: const InputDecoration(
    labelText: '简介',
    hintText: '请输入简介',
    border: OutlineInputBorder(),
  ),
)

// 数字输入
TextField(
  keyboardType: TextInputType.number,
  decoration: const InputDecoration(
    labelText: '年龄',
    border: OutlineInputBorder(),
  ),
)

// 带验证的 TextField
TextField(
  controller: _emailController,
  keyboardType: TextInputType.emailAddress,
  decoration: InputDecoration(
    labelText: '邮箱',
    hintText: '请输入邮箱',
    border: const OutlineInputBorder(),
    errorText: _emailError.isEmpty ? null : _emailError,
  ),
  onChanged: (value) {
    setState(() {
      _emailError = _validateEmail(value) ? '' : '邮箱格式不正确';
    });
  },
)

// 带前缀和后缀的 TextField
TextField(
  decoration: InputDecoration(
    labelText: '金额',
    prefixText: '¥ ',
    suffixText: '元',
    border: const OutlineInputBorder(),
  ),
)
技巧:使用 TextEditingController 可以更灵活地控制文本,包括设置初始值、获取文本、选择文本等。

2. TextFormField 表单输入框

TextFormField 是 TextField 的封装,提供了表单验证功能,适合用于表单场景。

使用示例

// 基本 TextFormField
TextFormField(
  decoration: const InputDecoration(
    labelText: '邮箱',
    border: OutlineInputBorder(),
  ),
  validator: (value) {
    if (value == null || value.isEmpty) {
      return '请输入邮箱';
    }
    if (!value.contains('@')) {
      return '邮箱格式不正确';
    }
    return null;
  },
)

// 完整的登录表单
Form(
  key: _formKey,
  child: Column(
    children: [
      TextFormField(
        controller: _usernameController,
        decoration: const InputDecoration(
          labelText: '用户名',
          prefixIcon: Icon(Icons.person),
          border: OutlineInputBorder(),
        ),
        validator: (value) {
          if (value == null || value.isEmpty) {
            return '请输入用户名';
          }
          if (value.length < 3) {
            return '用户名至少3个字符';
          }
          return null;
        },
      ),
      const SizedBox(height: 16),
      TextFormField(
        controller: _passwordController,
        obscureText: _obscurePassword,
        decoration: InputDecoration(
          labelText: '密码',
          prefixIcon: const Icon(Icons.lock),
          suffixIcon: IconButton(
            icon: Icon(
              _obscurePassword ? Icons.visibility : Icons.visibility_off,
            ),
            onPressed: () {
              setState(() {
                _obscurePassword = !_obscurePassword;
              });
            },
          ),
          border: const OutlineInputBorder(),
        ),
        validator: (value) {
          if (value == null || value.isEmpty) {
            return '请输入密码';
          }
          if (value.length < 6) {
            return '密码至少6个字符';
          }
          return null;
        },
      ),
      const SizedBox(height: 24),
      ElevatedButton(
        onPressed: () {
          if (_formKey.currentState!.validate()) {
            // 表单验证通过
            _login();
          }
        },
        child: const Text('登录'),
      ),
    ],
  ),
)

3. ElevatedButton 凸起按钮

ElevatedButton 是 Material Design 风格的凸起按钮,适合用于主要操作。

使用示例

// 基本 ElevatedButton
ElevatedButton(
  onPressed: () {
    print('按钮被点击');
  },
  child: const Text('点击我'),
)

// 带图标的按钮
ElevatedButton.icon(
  onPressed: () {},
  icon: const Icon(Icons.send),
  label: const Text('发送'),
)

// 自定义样式
ElevatedButton(
  onPressed: () {},
  style: ElevatedButton.styleFrom(
    backgroundColor: Colors.blue,
    foregroundColor: Colors.white,
    padding: const EdgeInsets.symmetric(
      horizontal: 24,
      vertical: 12,
    ),
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(8),
    ),
  ),
  child: const Text('自定义按钮'),
)

// 禁用状态
ElevatedButton(
  onPressed: null,
  child: const Text('禁用按钮'),
)

// 全宽按钮
SizedBox(
  width: double.infinity,
  child: ElevatedButton(
    onPressed: () {},
    style: ElevatedButton.styleFrom(
      padding: const EdgeInsets.symmetric(vertical: 16),
    ),
    child: const Text('全宽按钮'),
  ),
)

4. TextButton 文本按钮

TextButton 是扁平的文本按钮,适合用于次要操作或导航。

使用示例

// 基本 TextButton
TextButton(
  onPressed: () {
    print('文本按钮被点击');
  },
  child: const Text('取消'),
)

// 带图标的文本按钮
TextButton.icon(
  onPressed: () {},
  icon: const Icon(Icons.cancel),
  label: const Text('取消'),
)

// 自定义样式
TextButton(
  onPressed: () {},
  style: TextButton.styleFrom(
    foregroundColor: Colors.red,
    textStyle: const TextStyle(fontSize: 16),
  ),
  child: const Text('删除'),
)

5. OutlinedButton 轮廓按钮

OutlinedButton 是带边框的按钮,介于 ElevatedButton 和 TextButton 之间。

使用示例

// 基本 OutlinedButton
OutlinedButton(
  onPressed: () {},
  child: const Text('轮廓按钮'),
)

// 带图标的轮廓按钮
OutlinedButton.icon(
  onPressed: () {},
  icon: const Icon(Icons.share),
  label: const Text('分享'),
)

// 自定义样式
OutlinedButton(
  onPressed: () {},
  style: OutlinedButton.styleFrom(
    foregroundColor: Colors.blue,
    side: const BorderSide(
      color: Colors.blue,
      width: 2,
    ),
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(8),
    ),
  ),
  child: const Text('自定义轮廓按钮'),
)

6. IconButton 图标按钮

IconButton 是只包含图标的按钮,适合用于工具栏或图标操作。

使用示例

// 基本 IconButton
IconButton(
  onPressed: () {
    print('图标按钮被点击');
  },
  icon: const Icon(Icons.favorite),
  iconSize: 32,
  color: Colors.red,
)

// 带工具提示的图标按钮
IconButton(
  onPressed: () {},
  icon: const Icon(Icons.delete),
  tooltip: '删除',
  color: Colors.red,
)

// 不同大小的图标按钮
Row(
  children: [
    IconButton(
      onPressed: () {},
      icon: const Icon(Icons.star),
      iconSize: 24,
    ),
    IconButton(
      onPressed: () {},
      icon: const Icon(Icons.star),
      iconSize: 32,
    ),
    IconButton(
      onPressed: () {},
      icon: const Icon(Icons.star),
      iconSize: 48,
    ),
  ],
)

// 浮动操作按钮(FAB)
FloatingActionButton(
  onPressed: () {},
  child: const Icon(Icons.add),
)

// 大号 FAB
FloatingActionButton.extended(
  onPressed: () {},
  icon: const Icon(Icons.camera_alt),
  label: const Text('拍照'),
)

7. Checkbox 复选框

Checkbox 用于选择多个选项的状态。

使用示例

// 基本 Checkbox
bool _isChecked = false;

Checkbox(
  value: _isChecked,
  onChanged: (bool? value) {
    setState(() {
      _isChecked = value ?? false;
    });
  },
)

// 带标签的复选框
CheckboxListTile(
  title: const Text('我同意服务条款'),
  subtitle: const Text('请仔细阅读条款内容'),
  value: _agreeToTerms,
  onChanged: (bool? value) {
    setState(() {
      _agreeToTerms = value ?? false;
    });
  },
  controlAffinity: ListTileControlAffinity.leading,
)

// 三态复选框
TriStateCheckbox(
  value: _tristateValue,
  onChanged: (bool? value) {
    setState(() {
      _tristateValue = value;
    });
  },
)

8. Radio 单选按钮

Radio 用于从多个选项中选择一个。

使用示例

// 基本 Radio
enum Gender { male, female }

Gender? _gender = Gender.male;

Radio(
  value: Gender.male,
  groupValue: _gender,
  onChanged: (Gender? value) {
    setState(() {
      _gender = value;
    });
  },
)

// 带标签的单选按钮
RadioListTile(
  title: const Text('男'),
  subtitle: const Text('选择男性'),
  value: Gender.male,
  groupValue: _gender,
  onChanged: (Gender? value) {
    setState(() {
      _gender = value;
    });
  },
)

9. Switch 开关

Switch 用于切换开/关状态。

使用示例

// 基本 Switch
bool _isSwitched = false;

Switch(
  value: _isSwitched,
  onChanged: (bool value) {
    setState(() {
      _isSwitched = value;
    });
  },
)

// 带标签的开关
SwitchListTile(
  title: const Text('通知'),
  subtitle: const Text('接收推送通知'),
  value: _notificationsEnabled,
  onChanged: (bool value) {
    setState(() {
      _notificationsEnabled = value;
    });
  },
)

// 自定义颜色
Switch(
  value: _isSwitched,
  onChanged: (bool value) {
    setState(() {
      _isSwitched = value;
    });
  },
  activeColor: Colors.green,
  activeTrackColor: Colors.green.withOpacity(0.5),
)

10. Slider 滑块

Slider 用于选择一个范围内的数值。

使用示例

// 基本 Slider
double _sliderValue = 50.0;

Slider(
  value: _sliderValue,
  min: 0,
  max: 100,
  divisions: 10,
  label: _sliderValue.round().toString(),
  onChanged: (double value) {
    setState(() {
      _sliderValue = value;
    });
  },
)

// 带图标的滑块
SliderTheme(
  data: SliderTheme.of(context).copyWith(
    activeTrackColor: Colors.red,
    inactiveTrackColor: Colors.red.withOpacity(0.3),
    thumbColor: Colors.red,
    overlayColor: Colors.red.withOpacity(0.2),
  ),
  child: Slider(
    value: _sliderValue,
    min: 0,
    max: 100,
    onChanged: (double value) {
      setState(() {
        _sliderValue = value;
      });
    },
  ),
)

11. Dropdown 下拉选择

DropdownButton 用于从下拉列表中选择一个选项。

使用示例

// 基本 DropdownButton
String? _selectedItem;

DropdownButton(
  hint: const Text('请选择'),
  value: _selectedItem,
  items: const [
    DropdownMenuItem(
      value: 'Option 1',
      child: Text('选项 1'),
    ),
    DropdownMenuItem(
      value: 'Option 2',
      child: Text('选项 2'),
    ),
    DropdownMenuItem(
      value: 'Option 3',
      child: Text('选项 3'),
    ),
  ],
  onChanged: (String? value) {
    setState(() {
      _selectedItem = value;
    });
  },
)

// 带图标的下拉选择
DropdownButtonFormField(
  decoration: const InputDecoration(
    labelText: '城市',
    border: OutlineInputBorder(),
    prefixIcon: Icon(Icons.location_city),
  ),
  value: _selectedCity,
  items: ['北京', '上海', '广州', '深圳'].map((String city) {
    return DropdownMenuItem(
      value: city,
      child: Text(city),
    );
  }).toList(),
  onChanged: (String? value) {
    setState(() {
      _selectedCity = value;
    });
  },
)

输入组件最佳实践

  • 为所有输入组件添加清晰的标签和提示文本
  • 使用 TextEditingController 管理文本状态
  • 为密码字段添加显示/隐藏切换功能
  • 使用 Form 和 TextFormField 进行表单验证
  • 根据操作重要性选择合适的按钮类型
  • 为按钮添加合适的尺寸和间距
  • 使用 Material Design 3 的新特性提升用户体验
  • 为所有交互组件提供视觉反馈
提示:使用 Flutter 3.27+ 的 FormField 可以更灵活地实现自定义表单验证。
下一步:掌握输入组件后,继续学习列表组件和数据展示。