【Flutter实战】Flutter表单处理与验证完整指南

前言

表单处理看似简单,实则有很多细节。验证规则、错误提示、提交逻辑,每一项都需要仔细考虑。

我做过的表单从简单的登录注册,到复杂的多步骤向导,积累了不少经验。这篇文章我想分享表单处理的实践技巧。


一、表单基础组件

在这里插入图片描述

1.1 TextFormField基础

class BasicForm extends StatefulWidget {
  
  State<BasicForm> createState() => _BasicFormState();
}

class _BasicFormState extends State<BasicForm> {
  final _formKey = GlobalKey<FormState>();
  final _nameController = TextEditingController();
  final _emailController = TextEditingController();

  
  void dispose() {
    _nameController.dispose();
    _emailController.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            controller: _nameController,
            decoration: InputDecoration(
              labelText: '姓名',
              prefixIcon: Icon(Icons.person),
              border: OutlineInputBorder(),
            ),
            validator: (value) {
              if (value == null || value.isEmpty) {
                return '请输入姓名';
              }
              return null;
            },
          ),
          SizedBox(height: 16),
          TextFormField(
            controller: _emailController,
            decoration: InputDecoration(
              labelText: '邮箱',
              prefixIcon: Icon(Icons.email),
              border: OutlineInputBorder(),
            ),
            keyboardType: TextInputType.emailAddress,
            validator: (value) {
              if (value == null || value.isEmpty) {
                return '请输入邮箱';
              }
              if (!value.contains('@')) {
                return '请输入有效的邮箱地址';
              }
              return null;
            },
          ),
          SizedBox(height: 24),
          ElevatedButton(
            onPressed: _submitForm,
            child: Text('提交'),
          ),
        ],
      ),
    );
  }

  void _submitForm() {
    if (_formKey.currentState!.validate()) {
      print('姓名: ${_nameController.text}');
      print('邮箱: ${_emailController.text}');
    }
  }
}

1.2 表单字段类型

// 密码输入
TextFormField(
  obscureText: true,
  decoration: InputDecoration(
    labelText: '密码',
    suffixIcon: Icon(Icons.visibility),
  ),
)

// 多行输入
TextFormField(
  maxLines: 5,
  decoration: InputDecoration(
    labelText: '描述',
  ),
)

// 数字输入
TextFormField(
  keyboardType: TextInputType.number,
  decoration: InputDecoration(labelText: '年龄'),
)

二、表单验证封装

2.1 验证器封装

class Validators {
  static final RegExp _emailRegExp = RegExp(
    r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
  );

  static final RegExp _phoneRegExp = RegExp(
    r'^1[3-9]\d{9}$',
  );

  static String? required(String? value, [String message = '此项不能为空']) {
    if (value == null || value.isEmpty) {
      return message;
    }
    return null;
  }

  static String? email(String? value) {
    if (value == null || value.isEmpty) {
      return '请输入邮箱';
    }
    if (!_emailRegExp.hasMatch(value)) {
      return '请输入有效的邮箱地址';
    }
    return null;
  }

  static String? phone(String? value) {
    if (value == null || value.isEmpty) {
      return '请输入手机号';
    }
    if (!_phoneRegExp.hasMatch(value)) {
      return '请输入有效的手机号';
    }
    return null;
  }

  static String? length(String? value, int min, int max) {
    if (value == null || value.isEmpty) {
      return '请输入内容';
    }
    if (value.length < min || value.length > max) {
      return '长度应在$min$max之间';
    }
    return null;
  }
}

2.2 使用验证器

TextFormField(
  decoration: InputDecoration(labelText: '邮箱'),
  validator: Validators.email,
)

三、表单美化技巧

3.1 自定义边框

TextFormField(
  decoration: InputDecoration(
    labelText: '用户名',
    border: OutlineInputBorder(
      borderRadius: BorderRadius.circular(8),
    ),
    focusedBorder: OutlineInputBorder(
      borderRadius: BorderRadius.circular(8),
      borderSide: BorderSide(color: Colors.blue, width: 2),
    ),
    errorBorder: OutlineInputBorder(
      borderRadius: BorderRadius.circular(8),
      borderSide: BorderSide(color: Colors.red),
    ),
  ),
)

3.2 表单字段组件封装

class AppTextField extends StatelessWidget {
  final String label;
  final IconData? icon;
  final bool obscureText;
  final String? Function(String?)? validator;
  final TextEditingController? controller;

  const AppTextField({
    super.key,
    required this.label,
    this.icon,
    this.obscureText = false,
    this.validator,
    this.controller,
  });

  
  Widget build(BuildContext context) {
    return TextFormField(
      controller: controller,
      obscureText: obscureText,
      validator: validator,
      decoration: InputDecoration(
        labelText: label,
        prefixIcon: icon != null ? Icon(icon) : null,
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(8),
        ),
      ),
    );
  }
}

四、复杂表单处理

在这里插入图片描述

4.1 多步骤表单

class MultiStepForm extends StatefulWidget {
  
  State<MultiStepForm> createState() => _MultiStepFormState();
}

class _MultiStepFormState extends State<MultiStepForm> {
  int _currentStep = 0;
  final _formKeys = [
    GlobalKey<FormState>(),
    GlobalKey<FormState>(),
    GlobalKey<FormState>(),
  ];

  List<Step> get _steps => [
    Step(
      title: Text('基本信息'),
      content: Form(
        key: _formKeys[0],
        child: Column(
          children: [
            AppTextField(label: '姓名', validator: Validators.required),
            SizedBox(height: 16),
            AppTextField(label: '邮箱', validator: Validators.email),
          ],
        ),
      ),
      isActive: _currentStep >= 0,
    ),
    Step(
      title: Text('详细信息'),
      content: Form(
        key: _formKeys[1],
        child: Column(
          children: [
            AppTextField(label: '电话', validator: Validators.phone),
          ],
        ),
      ),
      isActive: _currentStep >= 1,
    ),
    Step(
      title: Text('确认'),
      content: Form(
        key: _formKeys[2],
        child: Column(
          children: [
            Text('请确认信息'),
          ],
        ),
      ),
      isActive: _currentStep >= 2,
    ),
  ];

  
  Widget build(BuildContext context) {
    return Stepper(
      currentStep: _currentStep,
      steps: _steps,
      onStepContinue: () {
        if (_currentStep < _steps.length - 1) {
          if (_formKeys[_currentStep].currentState!.validate()) {
            setState(() => _currentStep++);
          }
        }
      },
      onStepCancel: () {
        if (_currentStep > 0) {
          setState(() => _currentStep--);
        }
      },
    );
  }
}

4.2 动态表单

class DynamicForm extends StatefulWidget {
  
  State<DynamicForm> createState() => _DynamicFormState();
}

class _DynamicFormState extends State<DynamicForm> {
  final List<TextEditingController> _controllers = [];

  void _addItem() {
    setState(() {
      _controllers.add(TextEditingController());
    });
  }

  void _removeItem(int index) {
    setState(() {
      _controllers[index].dispose();
      _controllers.removeAt(index);
    });
  }

  
  void dispose() {
    for (var controller in _controllers) {
      controller.dispose();
    }
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        ..._controllers.asMap().entries.map((entry) {
          final index = entry.key;
          final controller = entry.value;
          return Padding(
            padding: const EdgeInsets.only(bottom: 16),
            child: Row(
              children: [
                Expanded(
                  child: TextFormField(
                    controller: controller,
                    decoration: InputDecoration(labelText: '项目 ${index + 1}'),
                  ),
                ),
                IconButton(
                  icon: Icon(Icons.delete),
                  onPressed: () => _removeItem(index),
                ),
              ],
            ),
          );
        }),
        ElevatedButton.icon(
          onPressed: _addItem,
          icon: Icon(Icons.add),
          label: Text('添加项目'),
        ),
      ],
    );
  }
}

总结

表单处理需要注意细节。

核心要点:

  1. 使用Form和GlobalKey管理表单
  2. 封装验证器提高复用性
  3. 提供清晰的错误提示
  4. 美化表单提升体验
  5. 处理好提交状态

最佳实践:

  • 实时验证反馈
  • 防重复提交
  • 保存草稿功能
  • 自动填充支持
  • 无障碍支持

好的表单设计让用户输入更顺畅。

欢迎加入开源鸿蒙跨平台社区:开源鸿蒙跨平台开发者社区

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐