gmana_value_objects 0.0.4
gmana_value_objects: ^0.0.4 copied to clipboard
Production-ready value objects with configurable validation for Email, Password, Text, and Number
gmana_value_objects #
Production-ready domain value objects with configurable validation for Email, Password, Text, and Number types, built on gmana.
Note: This package is pure Dart and perfectly framework independent. You can run these validation objects in your CLI APIs, dart servers, or native Flutter applications. Use
gmanafor low-level rules and field validators; usegmana_value_objectswhen you want typed domain validation and rich error models.
For a complete API guide with examples for every value object, validator, and error type, see doc/api.md.
🚀 Installation #
Add gmana_value_objects to your pubspec.yaml dependencies:
dependencies:
gmana_value_objects: ^0.0.4 # Please check pub.dev for the latest version
Or install it via CLI:
dart pub add gmana_value_objects
🎨 Features Overview #
- ✅ Type-safe validation: Extensively uses sealed error hierarchies allowing easy pattern-matching via
switch. - ✅ Configurable Ensembles: Custom
ValidationConfigs adapt identically required constraints directly (Strictness rules, pattern-overrides). - ✅ Domain Separated: Prevents 'string-ly typed' architectures by securely validating input the moment it enters the application domain.
- ✅ i18n ready: Localize error results via simple switch maps over the
ValidationErrorbase classes.
🧩 Usage Models #
Formulating Emails #
import 'package:gmana_value_objects/gmana_value_objects.dart';
// Basic Usage
final email = Email('user@example.com');
if (email.isValid) {
print('Email is safe: ${email.valueOrNull}');
} else {
print('Rejection Trigger: ${email.errorOrNull}');
}
// Configurable constraints: Automatically block temporary mail structures
final strictEmail = Email(
'user@tempmail.com',
config: EmailValidationConfig.strict(),
);
Guarding Passwords #
Never roll your own password complexity algorithms! Pre-tested templates assure compliance.
// Enforce standard enterprise leniency or strictness
final lenientPassword = Password('test', config: PasswordValidationConfig.lenient());
final strictPassword = Password('MyP@ssw0rd!2024', config: PasswordValidationConfig.strict());
// Custom Security Needs
final customPassword = Password(
'mypassword',
config: PasswordValidationConfig(
minLength: 12,
minComplexityScore: 3,
commonPasswords: {'mypassword', 'companyname123'},
),
);
Contextual Text Parsing #
Easily guard text models against dangerous payloads using standard presets or bespoke configurations.
final username = TextValue('john_doe', config: TextValidationConfig.username());
final firstName = TextValue('John', config: TextValidationConfig.name());
final description = TextValue('A longer description...', config: TextValidationConfig.mediumText());
// Custom Text validation regex and filtering
final filteredText = TextValue(
'Hello World',
config: TextValidationConfig(
minLength: 5,
maxLength: 50,
pattern: r'^[a-zA-Z\s]+$',
blacklistedWords: {'spam', 'banned'},
trimWhitespace: true,
),
);
Controlling Numbers #
Preventing negative integers magically without double.tryParse headaches.
final age = NumberValue('25', config: NumberValidationConfig.age());
final price = NumberValue('19.99', config: NumberValidationConfig.price());
final rating = NumberValue('4', config: NumberValidationConfig.rating());
final percentage = NumberValue('85.5', config: NumberValidationConfig.percentage());
// Create securely directly from num instead of parsing strings!
final quantity = NumberValue.fromNum(10, config: NumberValidationConfig.positiveInteger());
🎯 Clean Architecture Workflows #
Native Flutter Forms #
Take advantage of the natively-bundled DefaultValidationErrorMessages for super-fast prototypes!
import 'package:flutter/material.dart';
import 'package:gmana_value_objects/gmana_value_objects.dart';
class _SignUpFormState extends State<SignUpForm> {
Email? _email;
final _messages = DefaultValidationErrorMessages();
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: InputDecoration(
labelText: 'Email',
errorText: _email?.errorOrNull != null
? _messages.getMessage(_email!.errorOrNull!)
: null,
),
onChanged: (val) => setState(() => _email = Email(val)),
);
}
}
Tying cleanly to state providers (Riverpod etc.) #
Your provider logic should never leak Domain validation details to Presentation. Ensure the entire state holds safe values!
class SignUpNotifier extends StateNotifier<SignUpState> {
SignUpNotifier() : super(const SignUpState());
void onEmailChanged(String value) {
state = state.copyWith(email: Email(value));
}
Future<void> submit() async {
// Prevent interaction explicitly
if (state.email?.isValid != true) return;
// We securely unwrap valueOrNull securely knowing it is completely filtered.
await _authService.signUp(
email: state.email!.valueOrNull!,
);
}
}
I18N (Localizing your validation exceptions) #
Translating validation is extremely intuitive effectively leaning on Dart 3 pattern matching!
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:gmana_value_objects/gmana_value_objects.dart';
extension ValidationErrorL10n on ValidationError {
String localize(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return switch (this) {
EmailEmpty() => l10n.errorEmailEmpty,
EmailInvalidFormat() => l10n.errorEmailInvalid,
PasswordTooShort(:final minLength) => l10n.errorPasswordTooShort(minLength),
// Fallback
_ => DefaultValidationErrorMessages().getMessage(this),
};
}
}
⛓ Mapping to your Core Architectures (gmana) #
gmana_value_objects uses gmana's Either<ValidationError, T> under the hood and re-exports Either, Left, and Right, so the value-object API stays aligned with the rest of the gmana package family.
import 'package:gmana_value_objects/gmana_value_objects.dart' as vo;
sealed class Failure {}
final class ValidationFailure extends Failure {
final vo.ValidationError error;
ValidationFailure(this.error);
}
final class AppEmail {
final vo.Either<Failure, String> value;
factory AppEmail(String input) {
return AppEmail._(
// Safely swap value-object errors for native Domain Failure variants
vo.Email(input).value.mapLeft((error) => ValidationFailure(error)),
);
}
const AppEmail._(this.value);
}