在这里插入图片描述

案例概述

本案例展示如何构建一个简单的文本输入表单,包含姓名、邮箱、留言等字段。演示了 TextFormFieldForm 和基础表单验证的用法。

核心概念

1. Form 与 FormState

  • Form:表单容器,管理多个表单字段;
  • FormState:表单状态,提供 validate()save() 方法。

2. TextFormField

  • 带验证功能的文本输入框;
  • 支持 validator 回调进行字段验证;
  • 支持 onSaved 回调保存字段值。

3. 基础验证

  • 非空验证;
  • 格式验证(如邮箱格式);
  • 长度验证。

代码详解

1. 表单结构

Form(
  key: _formKey,
  child: Column(
    children: [
      TextFormField(...),
      TextFormField(...),
      ElevatedButton(onPressed: _submitForm, child: Text('提交')),
    ],
  ),
)

2. 字段验证

TextFormField(
  validator: (value) {
    if (value == null || value.isEmpty) {
      return '姓名不能为空';
    }
    return null;
  },
)

3. 表单提交

void _submitForm() {
  if (_formKey.currentState!.validate()) {
    _formKey.currentState!.save();
    // 处理提交
  }
}

高级话题:表单设计的最佳实践

1. 表单字段的组织

  • 逻辑分组:将相关字段分组显示;
  • 视觉层次:使用标题、间距等区分不同部分;
  • 响应式布局:在不同屏幕宽度上调整布局。

2. 表单验证的策略

  • 实时验证:在用户输入时验证(需要谨慎,避免过度提示);
  • 提交时验证:在用户点击提交时验证(推荐);
  • 混合验证:某些字段实时验证,某些字段提交时验证。

3. 错误提示的设计

TextFormField(
  decoration: InputDecoration(
    errorText: _errorMessage,
    errorStyle: TextStyle(color: Colors.red),
  ),
)

4. 表单加载状态

在提交表单时显示加载状态:

ElevatedButton(
  onPressed: _isLoading ? null : _submitForm,
  child: _isLoading ? CircularProgressIndicator() : Text('提交'),
)

5. 表单重置

void _resetForm() {
  _formKey.currentState!.reset();
}

6. 多步表单

对于复杂表单,可以分步骤显示:

if (_currentStep == 0) {
  // 显示第一步
} else if (_currentStep == 1) {
  // 显示第二步
}

7. 表单数据持久化

保存用户输入的数据,以便用户返回时恢复:


void initState() {
  super.initState();
  _loadSavedData();
}

Future<void> _loadSavedData() async {
  final prefs = await SharedPreferences.getInstance();
  _nameController.text = prefs.getString('name') ?? '';
}

8. 表单验证的高级技巧

跨字段验证(如确认密码):

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

9. 表单的键盘处理

TextFormField(
  textInputAction: TextInputAction.next,
  onFieldSubmitted: (value) {
    FocusScope.of(context).nextFocus();
  },
)

10. 表单的无障碍支持

TextFormField(
  decoration: InputDecoration(
    labelText: '邮箱',
    semanticCounterText: '邮箱地址输入框',
  ),
)

通过这些最佳实践,你可以构建出用户友好、易用的表单。

高级话题:表单设计的深度优化

1. 表单状态管理

使用 Provider 或 Riverpod 管理复杂表单状态:

class FormProvider extends ChangeNotifier {
  String _name = '';
  String? _nameError;
  bool _isSubmitting = false;

  void setName(String value) {
    _name = value;
    _nameError = _validateName(value);
    notifyListeners();
  }

  Future<void> submitForm() async {
    _isSubmitting = true;
    notifyListeners();
    try {
      await submitToServer(_name);
    } finally {
      _isSubmitting = false;
      notifyListeners();
    }
  }
}

2. 表单字段的自动填充

从本地存储或 API 预填充表单:


void initState() {
  super.initState();
  _loadUserData();
}

Future<void> _loadUserData() async {
  final user = await fetchUserProfile();
  _nameController.text = user.name;
  _emailController.text = user.email;
}

3. 表单的撤销/重做功能

class FormHistory {
  final List<Map<String, String>> _history = [];
  int _currentIndex = -1;

  void addState(Map<String, String> state) {
    _history.add(state);
    _currentIndex++;
  }

  void undo() {
    if (_currentIndex > 0) _currentIndex--;
  }

  void redo() {
    if (_currentIndex < _history.length - 1) _currentIndex++;
  }
}

4. 表单的自动保存

定期保存用户输入,防止数据丢失:

Timer? _autoSaveTimer;


void initState() {
  super.initState();
  _autoSaveTimer = Timer.periodic(Duration(seconds: 30), (_) {
    _autoSaveForm();
  });
}

Future<void> _autoSaveForm() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('form_data', jsonEncode({
    'name': _nameController.text,
    'email': _emailController.text,
  }));
}


void dispose() {
  _autoSaveTimer?.cancel();
  super.dispose();
}

5. 表单的多语言支持

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

6. 表单的动态字段

根据条件动态添加或移除字段:

List<String> _fields = ['name', 'email'];

void addField(String fieldName) {
  setState(() {
    _fields.add(fieldName);
  });
}

void removeField(String fieldName) {
  setState(() {
    _fields.remove(fieldName);
  });
}

Column(
  children: _fields.map((field) {
    return TextFormField(
      decoration: InputDecoration(labelText: field),
    );
  }).toList(),
)

7. 表单的条件验证

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

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

8. 表单的分步骤提交

对于长表单,分步骤收集数据:

int _currentStep = 0;

Stepper(
  currentStep: _currentStep,
  steps: [
    Step(
      title: Text('基本信息'),
      content: Column(children: [/* 第一步字段 */]),
    ),
    Step(
      title: Text('联系方式'),
      content: Column(children: [/* 第二步字段 */]),
    ),
  ],
  onStepContinue: () {
    if (_currentStep < 1) {
      setState(() => _currentStep++);
    }
  },
)

9. 表单的实时预览

显示表单数据的实时预览:

Column(
  children: [
    TextFormField(
      onChanged: (value) => setState(() {}),
    ),
    Container(
      padding: EdgeInsets.all(16),
      color: Colors.grey.shade100,
      child: Text('预览:${_nameController.text}'),
    ),
  ],
)

10. 表单的离线支持

在离线状态下保存表单,在线时同步:

Future<void> _submitFormWithOfflineSupport() async {
  final isOnline = await _checkConnectivity();
  if (isOnline) {
    await _submitToServer();
  } else {
    await _saveFormLocally();
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('离线保存,恢复连接后自动同步')),
    );
  }
}

通过这些高级优化,你可以构建出专业级的表单系统。

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

Logo

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

更多推荐