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

在这里插入图片描述

前言

在 Flutter 应用开发中,表单验证是一个高频且容易变复杂的场景。传统的 Form + GlobalKey 方式在大型应用中往往导致代码耦合度高、难以测试。

formz 提供了一种基于声明式编程模型的轻量级表单验证解决方案。它将每个输入字段封装为独立的验证对象,不仅简化了验证逻辑,还使得表单状态管理(如结合 Bloc 或 Provider)变得异常清晰。本文将详细介绍如何在 OpenHarmony 上使用 formz 构建健壮的表单。

一、formz 简介

1.1 核心理念

formz 的核心思想是将每个表单字段视为一个拥有自身状态的对象。每个字段包含:

  • 当前值 (value)
  • validation status (valid, invalid)
  • error message (如果有)

这种设计使得你可以在 UI 之外编写和测试验证逻辑,非常适合 MVVM 或 Bloc 架构。

1.2 OpenHarmony 适配情况

formz 是一个纯 Dart 逻辑库,没有任何原生平台依赖。因此,它可以在 OpenHarmony 上直接使用,无需任何特殊配置。

初始状态 (Initial)

用户输入 (User Input)

验证通过 (Validator Pass)

验证失败 (Validator Fail)

再次输入

再次输入

Pure

Dirty

Valid

Invalid

二、集成与基础用法

2.1 添加依赖

pubspec.yaml 中添加:

dependencies:
  formz: ^0.8.0

2.2 定义验证模型

你需要继承 FormzInput 来定义每个字段的验证规则。

import 'package:formz/formz.dart';

// 定义可能的验证错误
enum EmailValidationError { invalid }

// 创建 Email 输入模型
class Email extends FormzInput<String, EmailValidationError> {
  // 纯净状态(初始状态)
  const Email.pure() : super.pure('');
  
  // 修改后状态(脏状态)
  const Email.dirty([super.value = '']) : super.dirty();

  // 验证逻辑
  
  EmailValidationError? validator(String value) {
    return value.contains('@') ? null : EmailValidationError.invalid;
  }
}

三、核心 API 详解与示例

3.1 示例一:单一字段验证

演示如何使用刚才定义的 Email 模型。

void validateEmail() {
  // 1. 初始状态
  const emailPure = Email.pure();
  print('Is valid: ${emailPure.isValid}'); // false (因为初始为空且规则要有@)
  
  // 2. 输入变更
  const emailInvalid = Email.dirty('invalid-email');
  print('Error: ${emailInvalid.error}'); // EmailValidationError.invalid
  
  // 3. 有效输入
  const emailValid = Email.dirty('test@example.com');
  print('Is valid: ${emailValid.isValid}'); // true
  print('Value: ${emailValid.value}');
}

在这里插入图片描述

3.2 示例二:多字段联合验证

通常表单包含多个字段,formz 可以轻松检查整体状态。

import 'package:formz/formz.dart';

void checkFormStatus() {
  final email = Email.dirty('test@example.com');
  final password = Password.dirty('secure123'); // 假设定义了 Password 类

  // ✅ 推荐:使用 Formz.validate 检查列表
  final inputs = [email, password];
  final status = Formz.validate(inputs);
  
  if (status) {
    print('表单整体有效,可以提交');
  } else {
    print('表单存在无效字段');
  }
}

在这里插入图片描述

3.3 示例三:结合状态管理

这里展示如何在简单的 ChangeNotifier 中使用 formz

class LoginFormState extends ChangeNotifier {
  Email _email = const Email.pure();
  Password _password = const Password.pure();
  
  Email get email => _email;
  Password get password => _password;
  
  // 检查是否所有字段都有效
  bool get isValid => Formz.validate([_email, _password]);

  void emailChanged(String value) {
    _email = Email.dirty(value);
    notifyListeners();
  }

  void passwordChanged(String value) {
    _password = Password.dirty(value);
    notifyListeners();
  }
}

在这里插入图片描述

四、OpenHarmony 平台适配

4.1 输入法适配

在 OpenHarmony 上,处理键盘遮挡是常见问题。建议配合 SingleChildScrollViewScaffoldresizeToAvoidBottomInset 属性使用。

Scaffold(
  resizeToAvoidBottomInset: true, // 键盘弹出时调整大小
  body: SingleChildScrollView(
    child: LoginForm(),
  ),
)

4.2 焦点管理

OpenHarmony 对焦点控制支持良好。使用 FocusNode 可以提升用户体验,例如输完邮箱后点击键盘上的 “Next” 自动跳到密码框。

五、完整实战示例:登录表单

本示例构建一个完整的登录页面,包含邮箱和密码验证,以及提交按钮的状态控制。

5.1 示例代码

首先定义 password 模型:

enum PasswordValidationError { empty }

class Password extends FormzInput<String, PasswordValidationError> {
  const Password.pure() : super.pure('');
  const Password.dirty([super.value = '']) : super.dirty();

  
  PasswordValidationError? validator(String value) {
    return value.isNotEmpty ? null : PasswordValidationError.empty;
  }
}

完整页面代码:

import 'package:flutter/material.dart';
import 'package:formz/formz.dart';

// 引入之前定义的 Email 和 Password 模型...

void main() {
  runApp(const MaterialApp(home: LoginPage()));
}

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

  
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  final _key = GlobalKey<FormState>();
  late Email _email;
  late Password _password;
  bool _status = false; // 表单整体状态

  
  void initState() {
    super.initState();
    _email = const Email.pure();
    _password = const Password.pure();
  }

  void _onEmailChanged(String value) {
    setState(() {
      _email = Email.dirty(value);
      _status = Formz.validate([_email, _password]);
    });
  }

  void _onPasswordChanged(String value) {
    setState(() {
      _password = Password.dirty(value);
      _status = Formz.validate([_email, _password]);
    });
  }

  void _submit() {
    if (_status) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('登录中...')),
      );
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Formz Login 示例')),
      body: Padding(
        padding: const EdgeInsets.all(24.0),
        child: Form(
          key: _key,
          child: Column(
            children: [
              TextFormField(
                initialValue: _email.value,
                decoration: InputDecoration(
                  labelText: '邮箱',
                  errorText: _email.isPure 
                      ? null 
                      : (_email.error == EmailValidationError.invalid ? '无效的邮箱地址' : null),
                  border: const OutlineInputBorder(),
                ),
                onChanged: _onEmailChanged,
                keyboardType: TextInputType.emailAddress,
              ),
              const SizedBox(height: 16),
              TextFormField(
                initialValue: _password.value,
                decoration: InputDecoration(
                  labelText: '密码',
                  errorText: _password.isPure
                      ? null
                      : (_password.error == PasswordValidationError.empty ? '密码不能为空' : null),
                  border: const OutlineInputBorder(),
                ),
                obscureText: true,
                onChanged: _onPasswordChanged,
              ),
              const SizedBox(height: 24),
              SizedBox(
                width: double.infinity,
                height: 48,
                child: ElevatedButton(
                  onPressed: _status ? _submit : null, // 仅当有效时启用
                  child: const Text('登录'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

在这里插入图片描述

六、总结

formz 通过将验证逻辑与 UI 解耦,极大地提升了代码的可维护性和测试性。在 OpenHarmony 开发中,它是构建复杂表单的理想选择,特别是配合 Bloc 或 Provider 使用时。

最佳实践

  1. 为每个输入字段创建独立的 FormzInput 类。
  2. 将验证逻辑移出 Widget,保持 UI 层的纯净。
  3. 利用 Formz.validate 统一管理表单提交状态。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐