在这里插入图片描述

前言

表单验证是商城应用中确保用户输入数据有效性的重要功能,广泛应用于登录注册、地址填写、支付信息等场景。一个设计良好的表单验证组件需要提供实时的输入反馈、清晰的错误提示,并支持多种验证规则。本文将详细介绍如何在Flutter和OpenHarmony平台上开发表单验证组件。

表单验证的设计需要考虑用户体验和数据安全。验证应该在合适的时机触发,既不能过于频繁打扰用户,也不能等到提交时才发现错误。错误提示应该明确告知用户问题所在和如何修正。

Flutter表单验证基础

首先定义验证规则:

typedef Validator = String? Function(String? value);

class Validators {
  static Validator required({String? message}) {
    return (value) {
      if (value == null || value.trim().isEmpty) {
        return message ?? '此项不能为空';
      }
      return null;
    };
  }

  static Validator minLength(int length, {String? message}) {
    return (value) {
      if (value != null && value.length < length) {
        return message ?? '至少输入$length个字符';
      }
      return null;
    };
  }

  static Validator maxLength(int length, {String? message}) {
    return (value) {
      if (value != null && value.length > length) {
        return message ?? '最多输入$length个字符';
      }
      return null;
    };
  }
}

Validators类提供了常用的验证规则工厂方法。每个方法返回一个Validator函数,接收输入值并返回错误信息或null。required验证非空,minLength和maxLength验证长度范围。message参数允许自定义错误提示。这种设计使验证规则可以灵活组合。

更多验证规则:

static Validator phone({String? message}) {
  return (value) {
    if (value == null || value.isEmpty) return null;
    final regex = RegExp(r'^1[3-9]\d{9}$');
    if (!regex.hasMatch(value)) {
      return message ?? '请输入正确的手机号';
    }
    return null;
  };
}

static Validator email({String? message}) {
  return (value) {
    if (value == null || value.isEmpty) return null;
    final regex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
    if (!regex.hasMatch(value)) {
      return message ?? '请输入正确的邮箱地址';
    }
    return null;
  };
}

static Validator pattern(RegExp regex, {String? message}) {
  return (value) {
    if (value == null || value.isEmpty) return null;
    if (!regex.hasMatch(value)) {
      return message ?? '格式不正确';
    }
    return null;
  };
}

phone验证中国大陆手机号格式,email验证邮箱格式,pattern支持自定义正则表达式。当输入为空时返回null,让required规则处理空值验证。这种设计使验证规则可以独立使用或组合使用。

组合验证器

static Validator compose(List<Validator> validators) {
  return (value) {
    for (final validator in validators) {
      final error = validator(value);
      if (error != null) {
        return error;
      }
    }
    return null;
  };
}

compose方法将多个验证规则组合成一个。按顺序执行验证,遇到第一个错误就返回。这种设计让开发者可以为一个输入框设置多个验证规则,如同时验证非空和格式。

验证输入框组件

class ValidatedTextField extends StatefulWidget {
  final String? label;
  final String? hintText;
  final Validator? validator;
  final TextEditingController? controller;
  final bool obscureText;
  final TextInputType? keyboardType;
  final ValueChanged<String>? onChanged;

  const ValidatedTextField({
    Key? key,
    this.label,
    this.hintText,
    this.validator,
    this.controller,
    this.obscureText = false,
    this.keyboardType,
    this.onChanged,
  }) : super(key: key);

  
  State<ValidatedTextField> createState() => _ValidatedTextFieldState();
}

class _ValidatedTextFieldState extends State<ValidatedTextField> {
  String? _errorText;
  bool _hasInteracted = false;

  void _validate(String? value) {
    if (!_hasInteracted) return;
    
    setState(() {
      _errorText = widget.validator?.call(value);
    });
  }

  
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        if (widget.label != null) ...[
          Text(
            widget.label!,
            style: const TextStyle(
              fontSize: 14,
              color: Color(0xFF333333),
            ),
          ),
          const SizedBox(height: 8),
        ],
        _buildTextField(),
        if (_errorText != null) ...[
          const SizedBox(height: 4),
          _buildErrorText(),
        ],
      ],
    );
  }
}

ValidatedTextField组件封装了带验证功能的输入框。_hasInteracted标记用户是否已经与输入框交互,避免初始状态就显示错误。_validate方法执行验证并更新错误状态。Column垂直排列标签、输入框和错误提示。条件渲染确保只在有错误时显示错误文字。

输入框构建:

Widget _buildTextField() {
  return TextField(
    controller: widget.controller,
    obscureText: widget.obscureText,
    keyboardType: widget.keyboardType,
    decoration: InputDecoration(
      hintText: widget.hintText,
      hintStyle: const TextStyle(
        fontSize: 14,
        color: Color(0xFFCCCCCC),
      ),
      contentPadding: const EdgeInsets.all(12),
      border: OutlineInputBorder(
        borderRadius: BorderRadius.circular(8),
        borderSide: BorderSide(
          color: _errorText != null 
            ? const Color(0xFFE53935) 
            : const Color(0xFFEEEEEE),
        ),
      ),
      focusedBorder: OutlineInputBorder(
        borderRadius: BorderRadius.circular(8),
        borderSide: BorderSide(
          color: _errorText != null 
            ? const Color(0xFFE53935) 
            : const Color(0xFFE53935),
        ),
      ),
      errorBorder: OutlineInputBorder(
        borderRadius: BorderRadius.circular(8),
        borderSide: const BorderSide(color: Color(0xFFE53935)),
      ),
    ),
    onChanged: (value) {
      _hasInteracted = true;
      _validate(value);
      widget.onChanged?.call(value);
    },
    onTap: () {
      _hasInteracted = true;
    },
  );
}

Widget _buildErrorText() {
  return Row(
    children: [
      const Icon(
        Icons.error_outline,
        size: 14,
        color: Color(0xFFE53935),
      ),
      const SizedBox(width: 4),
      Text(
        _errorText!,
        style: const TextStyle(
          fontSize: 12,
          color: Color(0xFFE53935),
        ),
      ),
    ],
  );
}

输入框边框颜色根据错误状态变化,有错误时显示红色边框。onChanged在输入变化时触发验证,onTap在点击时标记已交互。错误提示使用红色图标和文字,Row水平排列使提示更加醒目。这种设计提供了清晰的实时验证反馈。

OpenHarmony表单验证实现

@Component
struct ValidatedTextField {
  @State errorText: string = ''
  @State hasInteracted: boolean = false
  @Prop label: string = ''
  @Prop hintText: string = ''
  @Prop value: string = ''
  private validator: (value: string) => string = () => ''
  private onChanged: (value: string) => void = () => {}

  build() {
    Column() {
      if (this.label) {
        Text(this.label)
          .fontSize(14)
          .fontColor('#333333')
          .margin({ bottom: 8 })
      }
      
      this.InputField()
      
      if (this.errorText) {
        this.ErrorText()
      }
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }

  validate(value: string) {
    if (!this.hasInteracted) return
    this.errorText = this.validator(value)
  }
}

OpenHarmony的验证输入框使用@State管理错误状态和交互状态。@Prop接收标签、提示文字和当前值。validator是验证函数,onChanged是值变化回调。Column垂直排列标签、输入框和错误提示。条件渲染在有错误时显示错误文字。

输入框ArkUI实现:

@Builder
InputField() {
  TextInput({ placeholder: this.hintText, text: this.value })
    .width('100%')
    .height(44)
    .padding({ left: 12, right: 12 })
    .borderRadius(8)
    .border({
      width: 1,
      color: this.errorText ? '#E53935' : '#EEEEEE'
    })
    .placeholderColor('#CCCCCC')
    .onChange((value: string) => {
      this.hasInteracted = true
      this.validate(value)
      this.onChanged(value)
    })
    .onFocus(() => {
      this.hasInteracted = true
    })
}

@Builder
ErrorText() {
  Row() {
    Image($r('app.media.error'))
      .width(14)
      .height(14)
    
    Text(this.errorText)
      .fontSize(12)
      .fontColor('#E53935')
      .margin({ left: 4 })
  }
  .margin({ top: 4 })
}

TextInput组件创建输入框,border颜色根据错误状态变化。onChange在输入变化时触发验证,onFocus在获得焦点时标记已交互。ErrorText使用Row水平排列错误图标和文字。这种实现方式与Flutter版本功能一致。

表单组件

class ValidatedForm extends StatefulWidget {
  final List<Widget> children;
  final VoidCallback? onSubmit;

  const ValidatedForm({
    Key? key,
    required this.children,
    this.onSubmit,
  }) : super(key: key);

  
  State<ValidatedForm> createState() => ValidatedFormState();
}

class ValidatedFormState extends State<ValidatedForm> {
  final _formKey = GlobalKey<FormState>();

  bool validate() {
    return _formKey.currentState?.validate() ?? false;
  }

  void submit() {
    if (validate()) {
      widget.onSubmit?.call();
    }
  }

  
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: widget.children,
      ),
    );
  }
}

ValidatedForm组件封装了Flutter的Form组件,提供统一的验证和提交功能。_formKey用于访问Form的状态。validate方法触发所有子输入框的验证,submit方法在验证通过后执行提交回调。这种设计使表单的验证和提交更加便捷。

表单使用示例

class LoginForm extends StatefulWidget {
  
  State<LoginForm> createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
  final _formKey = GlobalKey<ValidatedFormState>();
  final _phoneController = TextEditingController();
  final _passwordController = TextEditingController();

  
  Widget build(BuildContext context) {
    return ValidatedForm(
      key: _formKey,
      children: [
        ValidatedTextField(
          label: '手机号',
          hintText: '请输入手机号',
          controller: _phoneController,
          keyboardType: TextInputType.phone,
          validator: Validators.compose([
            Validators.required(message: '请输入手机号'),
            Validators.phone(),
          ]),
        ),
        const SizedBox(height: 16),
        ValidatedTextField(
          label: '密码',
          hintText: '请输入密码',
          controller: _passwordController,
          obscureText: true,
          validator: Validators.compose([
            Validators.required(message: '请输入密码'),
            Validators.minLength(6, message: '密码至少6位'),
          ]),
        ),
        const SizedBox(height: 24),
        ElevatedButton(
          onPressed: () => _formKey.currentState?.submit(),
          child: const Text('登录'),
        ),
      ],
      onSubmit: _handleLogin,
    );
  }

  void _handleLogin() {
    // 执行登录逻辑
  }
}

登录表单使用ValidatedForm包裹两个ValidatedTextField。手机号输入框使用required和phone组合验证,密码输入框使用required和minLength组合验证。点击登录按钮时调用表单的submit方法,验证通过后执行登录逻辑。这种使用方式简洁直观。

总结

本文详细介绍了Flutter和OpenHarmony平台上表单验证组件的开发过程。表单验证作为确保数据有效性的重要功能,其设计质量直接影响用户的填写体验和数据质量。通过验证规则、验证输入框、表单组件等的合理设计,我们为用户提供了清晰的实时验证反馈。在实际项目中,还可以进一步添加异步验证、表单状态管理等功能。

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

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐