← → or space · progress saves for Continue on the roadmap
Goal
Combine null-safe checks and small validators you can reuse.
Step 1 - Field error alias
typedef FieldError = String?;Step 2 - Present check
FieldError requirePresent(String? value) {
if (value == null || value.trim().isEmpty) {
return 'required';
}
return null;
}Step 3 - Email
FieldError validateEmail(String? value) {
if (value == null || value.trim().isEmpty) {
return 'required';
}
final v = value.trim();
if (!v.contains('@')) return 'invalid email';
return null;
}Step 4 - Password rules
FieldError validatePassword(String? value) {
if (value == null || value.trim().isEmpty) {
return 'required';
}
final v = value.trim();
if (v.length < 8) return 'min length 8';
if (!RegExp(r'[0-9]').hasMatch(v)) return 'need a digit';
return null;
}Step 5 - Aggregate
Map<String, FieldError> validateSignUp({String? email, String? password}) {
return {
'email': validateEmail(email),
'password': validatePassword(password),
};
}
void main() {
final errors = validateSignUp(email: 'bad', password: 'short');
print(errors);
}Practice tasks
- Return a record
(bool ok, Map<String, String> errors)where the map only contains keys that failed. - Add
validatePasswordConfirm(String? password, String? confirm). - Add maximum length checks with one shared helper
FieldError? lengthLimit(String trimmed, int max).