在这里插入图片描述

案例概述

本案例展示如何构建一个包含多个验证规则的表单,包括用户名长度验证、密码强度验证、密码一致性验证、手机号格式验证等。

核心概念

1. 验证规则

  • 长度验证:检查字符串长度;
  • 格式验证:使用正则表达式检查格式;
  • 跨字段验证:比较多个字段的值。

2. 正则表达式

  • RegExp(r'[A-Z]'):检查是否包含大写字母;
  • RegExp(r'[0-9]'):检查是否包含数字;
  • RegExp(r'^1[3-9]\d{9}$'):检查手机号格式。

3. 错误消息

  • 清晰的错误提示;
  • 指导用户如何修正。

代码详解

1. 用户名验证

validator: (value) {
  if (value == null || value.isEmpty) return '用户名不能为空';
  if (value.length < 3) return '用户名至少 3 个字符';
  if (value.length > 20) return '用户名最多 20 个字符';
  return null;
}

2. 密码强度验证

validator: (value) {
  if (value == null || value.isEmpty) return '密码不能为空';
  if (value.length < 6) return '密码至少 6 个字符';
  if (!value.contains(RegExp(r'[A-Z]'))) return '密码必须包含大写字母';
  if (!value.contains(RegExp(r'[0-9]'))) return '密码必须包含数字';
  return null;
}

3. 跨字段验证

validator: (value) {
  if (value != _password) return '两次输入的密码不一致';
  return null;
}

4. 手机号验证

validator: (value) {
  if (!RegExp(r'^1[3-9]\d{9}$').hasMatch(value)) {
    return '请输入有效的手机号';
  }
  return null;
}

高级话题:表单验证的深度应用

1. 异步验证

某些验证需要调用服务器 API:

validator: (value) async {
  if (value == null || value.isEmpty) return '用户名不能为空';
  final isAvailable = await checkUsernameAvailability(value);
  if (!isAvailable) return '用户名已被占用';
  return null;
}

2. 自定义验证器

创建可复用的验证函数:

String? validateEmail(String? value) {
  if (value == null || value.isEmpty) return '邮箱不能为空';
  if (!value.contains('@')) return '请输入有效的邮箱';
  return null;
}

TextFormField(
  validator: validateEmail,
)

3. 条件验证

根据其他字段的值进行验证:

validator: (value) {
  if (_userType == 'business' && (value == null || value.isEmpty)) {
    return '企业用户必须填写营业执照';
  }
  return null;
}

4. 实时验证反馈

在用户输入时显示验证状态:

TextFormField(
  onChanged: (value) {
    setState(() {
      _passwordStrength = calculateStrength(value);
    });
  },
  decoration: InputDecoration(
    suffixIcon: _passwordStrength > 0.7
        ? Icon(Icons.check_circle, color: Colors.green)
        : null,
  ),
)

5. 密码强度指示器

LinearProgressIndicator(
  value: _passwordStrength,
  backgroundColor: Colors.grey.shade300,
  valueColor: AlwaysStoppedAnimation(
    _passwordStrength > 0.7 ? Colors.green : Colors.orange,
  ),
)

6. 表单验证的性能优化

避免在每次输入时都进行昂贵的验证:

Timer? _debounce;

void _onUsernameChanged(String value) {
  _debounce?.cancel();
  _debounce = Timer(const Duration(milliseconds: 500), () {
    _validateUsername(value);
  });
}

7. 多语言错误提示

String getErrorMessage(String key, BuildContext context) {
  final locale = Localizations.localeOf(context);
  if (locale.languageCode == 'zh') {
    return {
      'empty': '不能为空',
      'invalid': '格式不正确',
    }[key] ?? '';
  }
  return {
    'empty': 'Cannot be empty',
    'invalid': 'Invalid format',
  }[key] ?? '';
}

8. 表单验证的状态管理

使用 Provider 或其他状态管理库管理表单状态:

class FormProvider extends ChangeNotifier {
  String _username = '';
  String? _usernameError;

  void setUsername(String value) {
    _username = value;
    _usernameError = validateUsername(value);
    notifyListeners();
  }
}

9. 表单提交的错误处理

void _submitForm() async {
  if (_formKey.currentState!.validate()) {
    try {
      await submitForm(_formData);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('提交成功')),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('提交失败:$e')),
      );
    }
  }
}

10. 表单验证的测试

test('验证用户名长度', () {
  expect(validateUsername('ab'), isNotNull);  // 太短
  expect(validateUsername('abc'), isNull);    // 正确
  expect(validateUsername('a' * 21), isNotNull);  // 太长
});

通过掌握这些高级验证技巧,你可以构建出健壮、用户友好的表单系统。

高级话题:表单验证的企业级应用

1. 验证规则引擎

创建可复用的验证规则:

class ValidationRule {
  final String name;
  final String Function(String?) validator;

  ValidationRule({required this.name, required this.validator});
}

final emailRule = ValidationRule(
  name: 'email',
  validator: (value) {
    if (value == null || value.isEmpty) return '邮箱不能为空';
    if (!value.contains('@')) return '邮箱格式不正确';
    return null;
  },
);

2. 服务器端验证

与后端 API 协作进行验证:

Future<String?> validateUsernameOnServer(String username) async {
  try {
    final response = await http.post(
      Uri.parse('https://api.example.com/validate-username'),
      body: {'username': username},
    );
    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      return data['available'] ? null : '用户名已被占用';
    }
  } catch (e) {
    return '验证失败,请稍后重试';
  }
  return null;
}

3. 验证结果的可视化反馈

TextFormField(
  onChanged: (value) {
    setState(() {
      _validationState = _validateField(value);
    });
  },
  decoration: InputDecoration(
    suffixIcon: _validationState == null
        ? Icon(Icons.check_circle, color: Colors.green)
        : _validationState!.isNotEmpty
            ? Icon(Icons.error_circle, color: Colors.red)
            : null,
  ),
)

4. 批量验证

一次验证多个字段:

Map<String, String?> validateAllFields() {
  return {
    'username': validateUsername(_usernameController.text),
    'password': validatePassword(_passwordController.text),
    'email': validateEmail(_emailController.text),
  };
}

bool hasErrors(Map<String, String?> results) {
  return results.values.any((error) => error != null);
}

5. 验证的国际化

支持多语言验证错误提示:

class ValidationMessages {
  static final Map<String, Map<String, String>> messages = {
    'zh': {
      'empty': '不能为空',
      'invalid_email': '邮箱格式不正确',
      'password_weak': '密码强度不足',
    },
    'en': {
      'empty': 'Cannot be empty',
      'invalid_email': 'Invalid email format',
      'password_weak': 'Password is too weak',
    },
  };

  static String get(String key, String locale) {
    return messages[locale]?[key] ?? messages['en']![key]!;
  }
}

6. 验证的性能优化

使用防抖避免频繁验证:

Timer? _validationDebounce;

void _onFieldChanged(String value) {
  _validationDebounce?.cancel();
  _validationDebounce = Timer(Duration(milliseconds: 500), () {
    setState(() {
      _fieldError = validateField(value);
    });
  });
}

7. 验证的缓存

缓存验证结果,避免重复验证:

final Map<String, String?> _validationCache = {};

String? validateWithCache(String fieldName, String value) {
  final cacheKey = '$fieldName:$value';
  if (_validationCache.containsKey(cacheKey)) {
    return _validationCache[cacheKey];
  }
  final result = _validateField(value);
  _validationCache[cacheKey] = result;
  return result;
}

8. 验证的链式调用

class FieldValidator {
  String? _error;

  FieldValidator notEmpty(String value, String message) {
    if (_error == null && (value.isEmpty)) {
      _error = message;
    }
    return this;
  }

  FieldValidator minLength(String value, int length, String message) {
    if (_error == null && value.length < length) {
      _error = message;
    }
    return this;
  }

  String? validate() => _error;
}

// 使用
final error = FieldValidator()
    .notEmpty(_username, '用户名不能为空')
    .minLength(_username, 3, '用户名至少 3 个字符')
    .validate();

9. 验证的测试

test('验证邮箱格式', () {
  expect(validateEmail(''), isNotNull);
  expect(validateEmail('invalid'), isNotNull);
  expect(validateEmail('test@example.com'), isNull);
});

test('验证密码强度', () {
  expect(validatePassword('123'), isNotNull);  // 太弱
  expect(validatePassword('Abc123!@'), isNull);  // 强
});

10. 验证的错误恢复

void _submitForm() {
  final errors = validateAllFields();
  if (hasErrors(errors)) {
    _showErrorDialog(errors);
    // 自动聚焦到第一个错误字段
    _focusFirstErrorField(errors);
  } else {
    _submitToServer();
  }
}

通过这些企业级验证技巧,你可以构建出生产级别的表单系统。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐