去年在开发社区接触到OpenHarmony后,一直想尝试用Flutter为其开发应用。最近终于抽空做了这个二手物品置换App,其中表单验证模块的踩坑经历特别值得分享。在跨平台开发中,表单验证看似基础实则暗藏玄机,特别是当Flutter遇到OpenHarmony时,需要兼顾两端特性。
这个App主要解决校园场景下的二手物品置换需求。用户需要填写物品信息(标题、描述、价格、图片等),这些字段都需要进行严格验证。比如价格必须大于0且不超过5位数,图片不能超过3张,标题长度要在5-30个字符之间。这些业务规则都需要通过表单验证来实现。
OpenHarmony作为新兴操作系统,其生态还在建设中。使用Flutter开发可以:
但需要注意:
采用分层验证策略:
dart复制class ItemForm {
final title = FormField<String>();
final price = FormField<double>();
final images = FormField<List<Image>>();
String? validate() {
return title.validate()
?? price.validate()
?? images.validate();
}
}
使用Flutter的Form和TextFormField组件构建基础表单:
dart复制TextFormField(
decoration: InputDecoration(labelText: '物品标题'),
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入标题';
}
if (value.length < 5 || value.length > 30) {
return '标题长度需在5-30个字符之间';
}
return null;
},
)
价格验证需要特殊处理:
dart复制validator: (value) {
final price = double.tryParse(value ?? '');
if (price == null) return '请输入有效价格';
if (price <= 0) return '价格必须大于0';
if (price > 99999) return '价格不能超过99999';
return null;
}
图片验证需要考虑:
dart复制validator: (images) {
if (images == null || images.isEmpty) {
return '请至少上传一张图片';
}
if (images.length > 3) {
return '最多上传3张图片';
}
for (var image in images) {
if (image.size > 2 * 1024 * 1024) {
return '单张图片不能超过2MB';
}
}
return null;
}
在OpenHarmony上需要特别注意:
解决方案:
dart复制import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:universal_io/io.dart';
void pickImage() {
if (kIsWeb) {
// Web实现
} else if (Platform.isAndroid || Platform.isIOS) {
// 移动端实现
} else {
// OpenHarmony实现
}
}
dart复制Timer? _debounce;
void _onTitleChanged(String value) {
if (_debounce?.isActive ?? false) _debounce?.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
_formKey.currentState?.validate();
});
}
dart复制ListView.builder(
itemCount: _fields.length,
itemBuilder: (context, index) {
return Visibility(
visible: index == _currentStep,
child: _fields[index],
);
},
)
症状:修改字段后错误提示不消失
解决:在initState中添加:
dart复制@override
void initState() {
super.initState();
_titleController.addListener(() {
_formKey.currentState?.validate();
});
}
对于可变字段(如标签列表),需要使用GlobalKey:
dart复制final _tagsFormKey = GlobalKey<FormFieldState<List<String>>>();
FormField<List<String>>(
key: _tagsFormKey,
builder: (field) {
return Column(
children: [
// 标签输入UI
if (field.hasError)
Text(
field.errorText!,
style: TextStyle(color: Colors.red),
),
],
);
},
validator: (tags) {
if (tags != null && tags.length > 5) {
return '最多添加5个标签';
}
return null;
},
)
比如检查标题是否重复:
dart复制validator: (value) async {
if (value == null || value.isEmpty) return null;
final exists = await ItemRepository.checkTitleExists(value);
return exists ? '该标题已存在' : null;
}
需要配合AsyncValidator使用:
dart复制Form(
autovalidateMode: AutovalidateMode.onUserInteraction,
child: AsyncValidator(
debounce: Duration(seconds: 1),
builder: (context, validator) {
return TextFormField(
validator: validator,
);
},
),
)
验证逻辑应该单独测试:
dart复制test('price validation', () {
expect(validatePrice(''), '请输入有效价格');
expect(validatePrice('0'), '价格必须大于0');
expect(validatePrice('100000'), '价格不能超过99999');
expect(validatePrice('99.5'), null);
});
使用flutter_driver测试完整流程:
dart复制test('submit valid form', () async {
await driver.tap(find.byValueKey('titleField'));
await driver.enterText('二手自行车');
await driver.tap(find.byValueKey('priceField'));
await driver.enterText('200');
await driver.tap(find.byValueKey('submitButton'));
await driver.waitFor(find.text('提交成功'));
});
特别注意:
创建可复用的验证规则:
dart复制class ItemValidators {
static FormFieldValidator<String> titleValidator = (value) {
if (value == null || value.isEmpty) return '请输入标题';
if (value.length < 5) return '标题太短';
if (value.length > 30) return '标题太长';
return null;
};
static FormFieldValidator<String> priceValidator = (value) {
final price = double.tryParse(value ?? '');
if (price == null) return '请输入有效价格';
if (price <= 0) return '价格必须大于0';
if (price > 99999) return '价格不能超过99999';
return null;
};
}
对于复杂表单,考虑使用状态管理:
dart复制class ItemFormModel with ChangeNotifier {
String _title = '';
double _price = 0;
String? get titleError => _validateTitle(_title);
String? get priceError => _validatePrice(_price);
bool get isValid => titleError == null && priceError == null;
void updateTitle(String value) {
_title = value;
notifyListeners();
}
// 其他字段...
}
多语言验证消息:
dart复制validator: (value) {
if (value == null || value.isEmpty) {
return S.of(context).titleRequired;
}
// ...
}
// 在arb文件中
{
"titleRequired": "请输入标题",
"priceInvalid": "请输入有效价格",
// ...
}
在OpenHarmony上开发Flutter应用的表单验证,最关键的是要理解平台差异并做好兼容性处理。实际开发中,建议先完成基础验证逻辑,再逐步添加业务规则验证,最后处理平台特定问题。验证错误提示要明确具体,避免通用模糊的错误信息。