formwise 1.1.0
formwise: ^1.1.0 copied to clipboard
Smart form fields for Flutter with auto-validation, auto-formatting, typo detection, and animated error feedback. Supports email, phone, credit card, URL, and custom fields.
formwise #
Smart form fields for Flutter with built-in validation, auto-formatting, typo detection, and animated error feedback.
Features #
- Smart Validators — Email (Levenshtein-based typo detection across 55+ domains), phone, URL, credit card (Luhn), postal code (20 countries), numeric range, pattern, and composable validators.
- Auto-Formatters — Credit card, phone (8 country presets), smart date (digit clamping), currency, postal code, custom mask, title case, uppercase/lowercase, trimmed.
- SmartTextFormField — Drop-in
TextFormFieldreplacement with 7 named constructors (.email(),.phone(),.creditCard(),.url(),.password(),.numeric(),.postalCode()), debounced validation, async validation, shake-on-error, and animated success/error indicators. - Animated Validation UX —
AnimatedValidationIcon(crossfade between states),StrengthIndicator(animated bar),AnimatedValidationBorder,PasswordStrengthcalculator. - SmartFormController — Manage multiple fields: validate all, reset, get/set values, track dirty state.
- Postal Code Validation — Format validation and auto-formatting for 20 countries (US, UK, Canada, Japan, Germany, France, Australia, India, Brazil, Nigeria, and more).
- ML Module (optional) — On-device TFLite email typo detection with automatic edit-distance fallback. Import
formwise_ml.dartseparately.
Installation #
dependencies:
formwise: ^1.1.0
flutter pub add formwise
Quick Start #
Named Constructors #
The fastest way to get a fully configured field:
import 'package:formwise/formwise.dart';
// Email with typo detection, lowercase formatting, email keyboard
SmartTextFormField.email(
name: 'email',
onTypoDetected: (suggestion) => print('Did you mean $suggestion?'),
)
// Phone with mask formatting and digit keyboard
SmartTextFormField.phone(
name: 'phone',
mask: '(###) ###-####',
)
// Credit card with Luhn validation and auto-formatting
SmartTextFormField.creditCard(name: 'card')
// Password with strength requirements
SmartTextFormField.password(
name: 'password',
minLength: 8,
requireUppercase: true,
requireDigit: true,
requireSpecialChar: true,
)
// URL with optional HTTPS enforcement
SmartTextFormField.url(name: 'website', requireHttps: true)
// Numeric with range validation
SmartTextFormField.numeric(name: 'age', min: 0, max: 150)
// Postal code with country-specific format
SmartTextFormField.postalCode(
name: 'zip',
country: PostalCountry.us,
)
Each named constructor pre-configures the validator, formatter, keyboard type, icon, label, and hint text.
Validators #
Email with Typo Detection #
Uses Levenshtein edit distance against 55+ known email providers. Catches typos like gmial.com → gmail.com, yhaoo.com → yahoo.com.
final validate = SmartValidators.email(
suggestCorrection: (suggestion) {
// suggestion = "user@gmail.com" when user types "user@gmial.com"
},
);
// Add your organization's domains to the known-good list:
final validate = SmartValidators.email(
customDomains: ['mycompany.com', 'mycompany.org'],
);
// Adjust sensitivity (default maxTypoDistance: 2):
final strict = SmartValidators.email(maxTypoDistance: 1);
Using EmailTypoDetector Directly
const detector = EmailTypoDetector();
// Check a domain
final match = detector.detectTypo('gmial.com');
// match?.suggested == 'gmail.com', match?.distance == 2
// Check a full email
final corrected = detector.suggestCorrection('user@gmial.com');
// corrected == 'user@gmail.com'
// Custom domain list
final custom = EmailTypoDetector(
domains: [...EmailTypoDetector.defaultDomains, 'mycompany.com'],
);
Phone #
SmartValidators.phone(
minDigits: 10,
maxDigits: 10,
allowCountryCode: false, // reject numbers starting with +
)
URL #
SmartValidators.url(
requireHttps: true,
allowedSchemes: ['https', 'ftp'],
)
Credit Card (Luhn) #
SmartValidators.creditCard()
// Detect brand:
final brand = SmartValidators.detectCardBrand('4111111111111111');
// CardBrand.visa, .mastercard, .amex, .discover, .unknown
Postal Code (20 Countries) #
// Validate for a specific country
final validate = PostalCodeValidator.forCountry(PostalCountry.uk);
validate('SW1A 1AA'); // null (valid)
validate('12345'); // 'Invalid postal code for UK (e.g. SW1A 1AA)'
// Auto-detect country format
final validate = PostalCodeValidator.autoDetect();
// Detect which countries match a code
final countries = PostalCodeValidator.detectCountry('12345');
// [PostalCountry.us, PostalCountry.germany, PostalCountry.france, ...]
Supported countries: US, UK, Canada, Japan, Germany, France, Australia, India, Brazil, Nigeria, Netherlands, Italy, Spain, South Korea, China, Russia, Mexico, Switzerland, Poland, Sweden.
Composing Validators #
SmartValidators.compose([
SmartValidators.required(errorMessage: 'Required'),
SmartValidators.minLength(length: 8),
SmartValidators.pattern(
regex: RegExp(r'[A-Z]'),
errorMessage: 'Must contain an uppercase letter',
),
])
Other Validators #
SmartValidators.required()
SmartValidators.minLength(length: 3)
SmartValidators.maxLength(length: 100)
SmartValidators.numericRange(min: 0, max: 999, allowDecimals: false)
SmartValidators.pattern(regex: RegExp(r'^\d+$'), errorMessage: 'Digits only')
Formatters #
Phone #
// Custom mask
SmartFormatters.phone(mask: '(###) ###-####')
// Country presets
SmartFormatters.phoneInternational(PhoneFormat.japan) // ###-####-####
SmartFormatters.phoneInternational(PhoneFormat.nigeria) // #### ### ####
SmartFormatters.phoneInternational(PhoneFormat.uk) // +44 #### ######
Presets: PhoneFormat.us, .uk, .japan, .nigeria, .germany, .india, .brazil, .international
Smart Date (with Digit Clamping) #
Auto-clamps month to 01–12 and day to 01–31 (adjusted per month) as the user types:
SmartFormatters.smartDate(DateFormat.mmddyyyy) // MM/DD/YYYY
SmartFormatters.smartDate(DateFormat.ddmmyyyy) // DD/MM/YYYY
SmartFormatters.smartDate(DateFormat.iso) // YYYY-MM-DD
SmartFormatters.smartDate(DateFormat.ddmmyyyyDot) // DD.MM.YYYY
SmartFormatters.smartDate(DateFormat.yyyymmdd) // YYYY/MM/DD
Credit Card #
SmartFormatters.creditCard() // 4-4-4-4 standard
SmartFormatters.creditCard(amexFormat: true) // 4-6-5 Amex
SmartFormatters.creditCard(autoDetectBrand: true) // switches by prefix
Currency #
SmartFormatters.currency() // $1,234.56
SmartFormatters.currency(symbol: '€', showSymbol: true)
SmartFormatters.currency(symbol: '', separator: '.', decimal: ',') // 1.234,56
Postal Code #
PostalCodeFormatter(PostalCountry.us) // 12345 or 12345-6789
PostalCodeFormatter(PostalCountry.japan) // 123-4567
PostalCodeFormatter(PostalCountry.uk) // SW1A 1AA
PostalCodeFormatter(PostalCountry.canada) // K1A 0B1
Other Formatters #
SmartFormatters.mask('###-##-####') // SSN: 123-45-6789
SmartFormatters.titleCase() // "john doe" → "John Doe"
SmartFormatters.uppercase()
SmartFormatters.lowercase()
SmartFormatters.trimmed() // collapses multiple spaces
SmartFormatters.digitsOnly()
Animated Validation UX #
AnimatedValidationIcon #
Crossfades between idle/spinner/checkmark/error with scale+fade transitions. Used internally by SmartTextFormField, or use standalone:
AnimatedValidationIcon(
state: ValidationState.valid, // .idle, .validating, .valid, .invalid
validColor: Colors.green,
invalidColor: Colors.red,
size: 20,
)
StrengthIndicator #
Animated bar from 0.0 to 1.0 with red→amber→green color interpolation:
StrengthIndicator(
strength: PasswordStrength.calculate(password),
label: PasswordStrength.label(strength), // "Very weak" → "Very strong"
height: 4,
)
PasswordStrength #
final strength = PasswordStrength.calculate('MyP@ss123'); // 0.0–1.0
final label = PasswordStrength.label(strength); // "Strong"
Scores based on: length, character variety (lowercase, uppercase, digits, special), uniqueness ratio.
AnimatedValidationBorder #
Wraps any widget with animated border color per validation state:
AnimatedValidationBorder(
state: ValidationState.valid,
child: TextField(...),
)
SmartFormController #
Manage multiple fields programmatically:
final controller = SmartFormController();
// Register fields via formController parameter:
SmartTextFormField.email(name: 'email', formController: controller)
SmartTextFormField.password(name: 'password', formController: controller)
// Validate all fields:
if (controller.validate()) {
final data = controller.values; // {'email': '...', 'password': '...'}
}
// Other operations:
controller.reset(); // restore initial values, clear errors
controller.isDirty; // any field modified?
controller.isValid; // all fields valid?
controller.errors; // {'email': 'error msg'} — only failing
controller.getValue('email'); // get one field
controller.setValue('email', 'new'); // set one field
Async Validation #
For server-side checks (username availability, etc.):
SmartTextFormField(
name: 'username',
asyncValidator: (value) async {
final available = await api.checkUsername(value!);
return available ? null : 'Username is already taken';
},
validationDebounce: Duration(milliseconds: 500),
)
Shows an animated spinner during validation, automatically cancels stale requests when the user keeps typing.
ML Module (Optional) #
For on-device ML email validation using TFLite. It's split into a separate formwise_ml.dart entry point so the ML API surface doesn't clutter the core import — but note that tflite_flutter is a package dependency either way, so its native binaries are bundled into your app regardless of whether you import formwise_ml.dart.
import 'package:formwise/formwise_ml.dart';
final validator = EmailMlValidator(
modelAsset: 'assets/models/email_typo.tflite',
labels: ['valid', 'gmail.com', 'yahoo.com', 'outlook.com'],
);
await validator.initialize();
final result = await validator.validateEmail('user@gmial.com');
// result.isValid == false
// result.suggestion == 'user@gmail.com'
// result.confidence == 0.92
Falls back to edit-distance detection when the model isn't available.
API Reference #
SmartValidators #
| Validator | Description |
|---|---|
email() |
Email with Levenshtein typo detection (55+ domains) |
phone() |
Phone number with configurable digit range |
url() |
URL with optional HTTPS enforcement |
creditCard() |
Credit card via Luhn algorithm |
numericRange() |
Number within min/max bounds |
pattern() |
Custom regex pattern |
required() |
Non-empty check |
minLength() |
Minimum character count |
maxLength() |
Maximum character count |
compose() |
Combine multiple validators (first error wins) |
detectCardBrand() |
Visa, Mastercard, Amex, Discover detection |
SmartFormatters #
| Formatter | Example Output |
|---|---|
creditCard() |
4111 1111 1111 1111 |
phone() |
(234) 567-8901 |
smartDate() |
12/31/2025 (with digit clamping) |
currency() |
$1,234.56 |
mask() |
###-##-#### → 123-45-6789 |
titleCase() |
John Doe |
uppercase() / lowercase() |
Case conversion |
trimmed() |
Collapse multiple spaces |
digitsOnly() |
Strip non-digits |
PostalCodeValidator #
| Method | Description |
|---|---|
forCountry() |
Validate against a specific country format |
autoDetect() |
Try all country formats |
detectCountry() |
List countries matching a code |
Animated Widgets #
| Widget | Description |
|---|---|
AnimatedValidationIcon |
Crossfade spinner/check/error icons |
StrengthIndicator |
Animated strength bar with color gradient |
AnimatedValidationBorder |
Animated border per validation state |
PasswordStrength |
Calculate and label password strength |
SmartTextFormField Named Constructors #
| Constructor | Pre-configured |
|---|---|
.email() |
Email validator + typo detection, lowercase, email keyboard |
.phone() |
Phone validator, mask formatter, phone keyboard |
.creditCard() |
Luhn validator, card formatter, number keyboard |
.url() |
URL validator, lowercase, URL keyboard |
.password() |
Composed strength validator, obscured text |
.numeric() |
Range validator, number keyboard |
.postalCode() |
Country validator + formatter, characters capitalization |
Example #
See the example app for a complete demo with 5 interactive screens.
License #
MIT License — see LICENSE for details.