Last updated:
0 purchases
rules
Introduction #
Rules - Powerful and feature-rich validation library for both Dart and Flutter.
Rules is a simple yet powerful and feature-rich validation library for both dart and flutter.
Features
Highly flexible
Easy to understand
Less boilerplate code
Custom error handling
Override individual errors
Flutter friendly
State management libraries friendly (Mobx example included)
Installation #
Go to https://pub.dev/packages/rules#-installing-tab- for the latest version of rules
To activate pre-commit hooks
$ git config core.hooksPath .githooks/
$ chmod +x .githooks/pre-commit
$ chmod +x .githooks/pre-push
copied to clipboard
Concept #
The Rules library has three parts
Rule: Basic rule
GroupRule: Group together and validate the basic rules
CombinedRule: Validate multiple basic rules and group rules together
Usage #
Import the library
import 'package:rules/rules.dart';
copied to clipboard
1. Rule (Basic Rule)
This is the basic building block, everything starts here.
Basic example
void main() {
const textFieldValue = '[email protected]';
final rule = Rule(
textFieldValue, // value; string and null are accepted
name: 'Text field', // placeholder value which will be used while displaying errors
);
print(rule.error);
// output: null
print(rule.hasError);
// output: false
}
copied to clipboard
Example 1
void main() {
const textFieldValue = ''; // or const textFieldValue = null;
final rule = Rule(
textFieldValue,
name: 'Text field',
isRequired: true,
);
print(rule.error);
// output: 'Text field is required'
print(rule.hasError);
// output: true
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
Example 2
void main() {
const textFieldValue = 'abc@xyz';
final rule = Rule(
textFieldValue,
name: 'Text field',
isRequired: true,
isEmail: true,
);
print(rule.error);
// output: 'Text field is not a valid email address'
print(rule.hasError);
// output: true
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
Available rules
String name; // mandatory
bool isRequired;
bool isEmail;
bool isUrl;
bool isPhone;
bool isIp;
bool isNumeric;
bool isNumericDecimal;
bool isAlphaSpace;
bool isAlphaNumeric;
bool isAlphaNumericSpace;
RegExp regex;
int length;
int minLength;
int maxLength;
double greaterThan;
double greaterThanEqualTo;
double lessThan;
double lessThanEqualTo;
double equalTo;
double notEqualTo;
List<double> equalToInList;
List<double> notEqualToInList;
String shouldMatch;
String shouldNotMatch;
bool shouldPass(String value);
List<String> inList;
List<String> notInList;
copied to clipboard
Available configurations
String customErrorText;
Map<String, String> customErrors;
RuleOptions options;
copied to clipboard
isRequired: bool
void main() {
const textFieldValue = '123';
final rule = Rule(
textFieldValue,
name: 'Text field',
isRequired: true, // throws an error for value = null or an empty string
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
IMPORTANT: If the value is empty or null and 'isRequired' is false or not set then no errors will be thrown for the subsequent constraints.
void main() {
const textFieldValue = ''; // or null
final rule = Rule(
textFieldValue,
name: 'Text field',
isRequired: false,
isEmail: true,
);
print(rule.hasError);
// output: false
}
copied to clipboard
isEmail: bool
void main() {
const textFieldValue = '[email protected]';
final rule = Rule(
textFieldValue,
name: 'Text field',
isEmail: true,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
isUrl: bool
void main() {
const textFieldValue = 'http://www.google.com';
final rule = Rule(
textFieldValue,
name: 'Text field',
isUrl: true,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
isPhone: bool
It recognizes the phone numbers starting with + or 0, no length limitations and handles #, x, ext, extension extension conventions.
void main() {
const textFieldValue = '+1-9090909090';
final rule = Rule(
textFieldValue,
name: 'Text field',
isPhone: true,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
isIp: bool
It accepts both IPv4 and IPv6 addresses.
void main() {
const textFieldValue = '1.1.1.1';
final rule = Rule(
textFieldValue,
name: 'Text field',
isIp: true,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
isNumeric: bool
It accepts both 0, positive and negative integers. Decimals are not allowed.
void main() {
const textFieldValue = '1'; // '-1', '0'
final rule = Rule(
textFieldValue,
name: 'Text field',
isNumeric: true,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
isNumericDecimal: bool
It accepts 0, postive and negative integers and decimals numbers.
void main() {
const textFieldValue = '10.01'; // '-10.01' or '0.001'
final rule = Rule(
textFieldValue,
name: 'Text field',
isNumericDecimal: true,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
isAlphaSpace: bool
It accepts multiple spaces and alphabets (both upper and lower case).
void main() {
const textFieldValue = 'Jane Doe';
final rule = Rule(
textFieldValue,
name: 'Text field',
isAlphaSpace: true,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
isAlphaNumeric: bool
It accepts alphabets (both upper and lower case) and numbers.
void main() {
const textFieldValue = 'username123';
final rule = Rule(
textFieldValue,
name: 'Text field',
isAlphaNumeric: true,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
isAlphaNumericSpace: bool
It accepts multiple spaces, alphabets (both upper and lower case) and numbers.
void main() {
const textFieldValue = 'Bread 20';
final rule = Rule(
textFieldValue,
name: 'Text field',
isAlphaNumericSpace: true,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
regex: RegExp
It accepts a custom regular expression string.
void main() {
const textFieldValue = 'abc123';
final rule = Rule(
textFieldValue,
name: 'Text field',
regex: RegExp(
r'^[a-zA-Z0-9\s]+$',
caseSensitive: false,
),
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
length: int
Defines the length of the input string.
void main() {
const textFieldValue = '9090909090';
final rule = Rule(
textFieldValue,
name: 'Phone Number',
length: 10,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
minLength: int
Defines the minimum length of the input string.
void main() {
const textFieldValue = 'abcd12345';
final rule = Rule(
textFieldValue,
name: 'Password',
minLength: 6,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
maxLength: int
Defines the maximum length of the input string.
void main() {
const textFieldValue = 'username12';
final rule = Rule(
textFieldValue,
name: 'Username',
minLength: 10,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
greaterThan: double
Checks if the input value is greater than the 'greaterThan' value.
if 'isNumeric' is not set then the 'isNumericDecimal' constraint will be applied automatically.
void main() {
const textFieldValue = '10';
final rule = Rule(
textFieldValue,
name: 'Text field',
greaterThan: 0,
isNumeric: true, // optional
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
greaterThanEqualTo: double
Checks if the input value is greater than or equal to the 'greaterThanEqualTo' value.
if 'isNumeric' is not set then 'isNumericDecimal' constraint will be applied automatically.
void main() {
const textFieldValue = '5.1';
final rule = Rule(
textFieldValue,
name: 'Text field',
greaterThanEqualTo: 5.0,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
lessThan: double
Checks if the input value is greater than or equal to the 'lessThan' value.
if 'isNumeric' is not set then 'isNumericDecimal' constraint will be applied automatically.
void main() {
const textFieldValue = '1';
final rule = Rule(
textFieldValue,
name: 'Text field',
lessThan: 2.0,
isNumericDecimal: true, // optional
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
lessThanEqualTo: double
Checks if the input value is greater than or equal to the 'lessThanEqualTo' value.
if 'isNumeric' is not set then 'isNumericDecimal' constraint will be applied automatically.
void main() {
const textFieldValue = '2.0';
final rule = Rule(
textFieldValue,
name: 'Text field',
lessThanEqualTo: 2.0,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
equalTo: double
Checks if the input value is equal to the 'equalTo' value.
if 'isNumeric' is not set then 'isNumericDecimal' constraint will be applied automatically.
void main() {
const textFieldValue = '2.0';
final rule = Rule(
textFieldValue,
name: 'Text field',
equalTo: 2.0,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
notEqualTo: double
Checks if the input value is equal to the 'notEqualTo' value.
It will throw an error if the values match.
if 'isNumeric' is not set then 'isNumericDecimal' constraint will be applied automatically.
void main() {
const textFieldValue = '2.0';
final rule = Rule(
textFieldValue,
name: 'Text field',
notEqualTo: 2.0,
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
equalToInList: List<double>
Checks if the input value matches with any of the 'equalToInList' values.
Note: This is a float comparison. 10.00 == 10 will be true.
void main() {
const textFieldValue = '10';
final rule = Rule(
textFieldValue,
name: 'Text field',
equalToInList: [0, 10],
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
notEqualToInList: List<double>
Checks if the input value matches with any of the 'notEqualToInList' values.
It will throw an error if there is a value match.
Note: This is a float comparison. 10.00 == 10 will be true.
void main() {
const textFieldValue = '10';
final rule = Rule(
textFieldValue,
name: 'Text field',
notEqualToInList: [0, 10],
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
shouldMatch: String
Checks if the input value matches with the 'shouldMatch' value.
Note: This is a string comparison.
void main() {
const textFieldValue = 'abc';
final rule = Rule(
textFieldValue,
name: 'Text field',
shouldMatch: 'abc',
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
shouldNotMatch: String
Checks if the input value matches with the 'shouldNotMatch' value.
It will throw an error if the values match.
Note: This is a string comparison.
void main() {
const textFieldValue = 'abc';
final rule = Rule(
textFieldValue,
name: 'Text field',
shouldNotMatch: 'xyz',
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
shouldPass: bool Function(String value)
Checks if the input value passes the given function check
It will throw an error if the function returns false
void main() {
const textFieldValue = 'abc';
final rule = Rule(
textFieldValue,
name: 'Text field',
shouldPass: (value) => value.contains('a') && value.contains('b')
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
inList: List<String>
Checks if the input value matches with any of the 'inList' values.
Note: This is a string comparison.
void main() {
const textFieldValue = 'abc';
final rule = Rule(
textFieldValue,
name: 'Text field',
inList: ['abc', 'xyz'],
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
notInList: List<String>
Checks if the input value matches with any of the 'notInList' values.
It will throw an error if there is a value match.
Note: This is a string comparison.
void main() {
const textFieldValue = 'abc';
final rule = Rule(
textFieldValue,
name: 'Text field',
notInList: ['abc', 'xyz'],
);
if (rule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
Custom Error
Default errors
'isRequired': '{name} is required'
'isEmail': '{name} is not a valid email address'
'isPhone': '{name} is not a valid phone number'
'isUrl': '{name} is not a valid URL'
'isIp': '{name} is not a valid IP address'
'isNumeric': '{name} is not a valid number'
'isNumericDecimal': '{name} is not a valid decimal number'
'isAlphaSpace': 'Only alphabets and spaces are allowed in {name}'
'isAlphaNumeric': 'Only alphabets and numbers are allowed in {name}'
'isAlphaNumericSpace': 'Only alphabets, numbers and spaces are allowed in {name}'
'regex': '{name} should match the pattern: {regex.pattern}'
'length': '{name} should be $length characters long'
'minLength': '{name} should contain at least $minLength characters'
'maxLength': '{name} should not exceed more than $maxLength characters'
'greaterThan': '{name} should be greater than $greaterThan'
'greaterThanEqualTo': '{name} should be greater than or equal to $greaterThanEqualTo'
'lessThan': '{name} should be less than $lessThan'
'lessThanEqualTo': '{name} should be less than or equal to $lessThanEqualTo'
'equalTo': '{name} should be equal to $equalTo'
'notEqualTo': '{name} should not be equal to $notEqualTo'
'equalToInList': '{name} should be equal to any of these values $equalToInList'
'notEqualToInList': '{name} should not be equal to any of these values $notEqualToInList'
'shouldMatch': '{name} should be same as $shouldMatch'
'shouldNotMatch': '{name} should not same as $shouldNotMatch'
'shouldPass': '{name} is invalid'
'inList': '{name} should be any of these values $inList'
'notInList': '{name} should not be any of these values $notInList'
copied to clipboard
Override the default errors
To override the error text of a particular option, set 'customErrors' as {'optionName': '<Error Text>'}.
The 'optionName' key should match with one of the 'Available rules' for overriding the error text.
Use {value} and {name} template variables in the 'customErrors' to display the input name and value respectively.
To override all default error texts set 'customErrorText'.
Note: 'customErrorText' will only override the default errors. 'customErrors' will be given the highest priority.
void main() {
const textFieldValue = ''; // or textFieldValue = 'xyz';
final rule = Rule(
textFieldValue,
name: 'Text field',
isRequired: true,
isEmail: true,
customErrors: {
'isRequired': 'Input is invalid.',
'isEmail': '{value} is an invalid value. Try another {name}.',
},
);
// when textFieldValue = '';
print(rule.error);
// output: Input is invalid.
// when textFieldValue = 'xyz';
print(rule.error);
// output: xyz is an invalid value. Try another Text field.
}
copied to clipboard
void main() {
const textFieldValue = '123';
final rule = Rule(
textFieldValue,
name: 'Text field',
isRequired: true,
isEmail: true,
customErrorText: 'Invalid email address',
);
print(rule.error);
// output: Invalid email address
}
copied to clipboard
Extension
Extend and override the constraints of a rule using 'copyWith' method
void main() {
const textFieldValue = 'abc';
final rule1 = Rule(
textFieldValue,
name: 'Text field 1',
isAlpha: true,
customErrorText: 'Only alphabets allowed',
);
final rule2 = rule1.copyWith(
name: 'Text field 2',
isEmail: true,
customErrorText: 'Invalid email address',
);
print(rule1.error);
// output: null
print(rule2.error);
// output: Invalid email address
}
copied to clipboard
The child rule will default to the constraint values of the parent unless they are set explicitly in the child.
void main() {
const textFieldValue = '';
// parent rule
final rule1 = Rule(
textFieldValue,
name: 'Text field',
isRequired: true,
customErrorText: 'Invalid input',
);
// child rule
final rule2 = rule1.copyWith(
isRequired: false,
);
print(rule1.error);
// output: Invalid input
print(rule2.error);
// output: null
}
copied to clipboard
2. GroupRule
Group together and validate the basic rules
Basic example
void main() {
const textFieldValue = '[email protected]';
final rule = Rule(
textFieldValue,
name: 'Text field',
);
final groupRule = GroupRule(
[rule], // value; List of Rule
name: 'Group name', // placeholder value which will be used while displaying errors
);
print(groupRule.error);
// output: null
print(groupRule.hasError);
// output: false
if (groupRule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
Example 1
void main() {
const textFieldValue1 = ''; // or const textFieldValue = null;
const textFieldValue2 = '';
final rule1 = Rule(
textFieldValue1,
name: 'Text field 1',
isRequired: true,
);
final rule2 = Rule(
textFieldValue2,
name: 'Text field 2',
isEmail: true,
);
final groupRule = GroupRule(
[rule1, rule2], // value; List of Rule
name: 'Group name', // placeholder value which will be used while displaying errors
);
print(groupRule.error);
// output: 'Text field 1 is required'
print(groupRule.hasError);
// output: true
}
copied to clipboard
Available rules
String name; // mandatory
bool requiredAll;
int requiredAtleast;
int maxAllowed;
copied to clipboard
Available configurations
String customErrorText;
Map<String, String> customErrors;
copied to clipboard
requiredAll: bool
Checks if all basic rules have a value.
void main() {
const textFieldValue1 = 'abc';
const textFieldValue2 = '';
final rule1 = Rule(
textFieldValue1,
name: 'Text field',
isRequired: true,
); // Validation OK
final rule2 = Rule(
textFieldValue2,
name: 'Text field',
); // Validation OK
final groupRule = GroupRule(
[rule1, rule2],
name: 'Group name',
requiredAll: true,
);
print(groupRule.error);
// output: All fields are mandatory in Group name
}
copied to clipboard
IMPORTANT: If any of the basic rules have validation errors then GroupRule will throw those errors first.
The group validation wouldn't happen until all basic rules pass the validation.
void main() {
const textFieldValue1 = 'abc'; // or const textFieldValue = null;
const textFieldValue2 = 'xyz@abc';
final rule1 = Rule(
textFieldValue1,
name: 'Text field 1',
isRequired: true,
); // Validation OK
final rule2 = Rule(
textFieldValue2,
name: 'Text field 2',
isEmail: true,
); // Validation FAILED
final groupRule = GroupRule([rule1, rule2], name: 'Group name');
print(groupRule.error);
// output: 'Text field 2 is not a valid email address'
print(groupRule.hasError);
// output: true
if (groupRule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
IMPORTANT: If the input basic rules list is an empty array or null and 'isRequiredAll' is false or not set then no errors will be thrown for the subsequent constraints.
void main() {
final groupRule = GroupRule([], name: 'Group name');
print(groupRule.hasError);
// output: false
}
copied to clipboard
requiredAtleast: int
Defines the minimum number of basic rules that should have a value.
The number of basic rules in a GroupRule should be greater than the 'requiredAtleast' value else it will throw an exception.
If the 'requiredAtleast' is 0 then it will pass the validation.
void main() {
const textFieldValue1 = 'abc';
const textFieldValue2 = '';
final rule1 = Rule(
textFieldValue1,
name: 'Text field',
); // Validation OK
final rule2 = Rule(
textFieldValue2,
name: 'Text field',
); // Validation OK
final groupRule = GroupRule(
[rule1, rule2],
name: 'Group name',
requiredAtleast: 2,
);
print(groupRule.error);
// output: At least 2 fields are required in Group name
}
copied to clipboard
void main() {
const textFieldValue1 = 'abc';
const textFieldValue2 = 'xyz';
final rule1 = Rule(
textFieldValue1,
name: 'Text field',
); // Validation OK
final rule2 = Rule(
textFieldValue2,
name: 'Text field',
); // Validation OK
final groupRule = GroupRule(
[rule1, rule2],
name: 'Group name',
requiredAtleast: 2,
);
print(groupRule.error);
// output: null
print(groupRule.hasError);
// output: false
}
copied to clipboard
maxAllowed: int
The maximum number of basic rules that are allowed to have a value.
void main() {
const textFieldValue1 = 'abc';
const textFieldValue2 = 'xyz';
final rule1 = Rule(
textFieldValue1,
name: 'Text field',
); // Validation OK
final rule2 = Rule(
textFieldValue2,
name: 'Text field',
); // Validation OK
final groupRule = GroupRule(
[rule1, rule2],
name: 'Group name',
maxAllowed: 1,
);
print(groupRule.error);
// output: A maximum of 1 field is allowed in Group name
}
copied to clipboard
void main() {
const textFieldValue1 = 'abc';
const textFieldValue2 = '';
final rule1 = Rule(
textFieldValue1,
name: 'Text field',
); // Validation OK
final rule2 = Rule(
textFieldValue2,
name: 'Text field',
); // Validation OK
final groupRule = GroupRule(
[rule1, rule2],
name: 'Group name',
maxAllowed: 1,
);
print(groupRule.error);
// output: null
print(groupRule.hasError);
// output: false
}
copied to clipboard
Custom Error
Default errors
Plurality for 'requiredAtleast' and 'maxAllowed' error texts will be automatically detected.
'requiredAll': 'All fields are mandatory in {name}'
'requiredAtleast': 'At least $requiredAtleast field(s) is/are required in {name}'
'maxAllowed': 'A maximum of $maxAllowed field(s) is/are allowed in {name}'
copied to clipboard
Override the default errors
To override the error text of a particular option, set 'customErrors' as {'optionName': '<Error Text>'}.
The 'optionName' key should match with one of the 'Available rules' for overriding the error text.
Use {value} and {name} template variables in the 'customErrors' to display the input name and value respectively.
To override all default error texts set 'customErrorText'.
Note: 'customErrorText' will only override the default errors. 'customErrors' will be given the highest priority.
void main() {
const textFieldValue1 = 'abc';
const textFieldValue2 = '';
final rule1 = Rule(
textFieldValue1,
name: 'Text field',
isRequired: true,
); // Validation OK
final rule2 = Rule(
textFieldValue2,
name: 'Text field',
); // Validation OK
final groupRule = GroupRule(
[rule1, rule2],
name: 'Group name',
requiredAll: true,
customErrors: {
'requiredAll': 'Input is invalid.',
},
);
print(groupRule.error);
// output: Input is invalid.
}
copied to clipboard
void main() {
const textFieldValue1 = 'abc';
const textFieldValue2 = '';
final rule1 = Rule(
textFieldValue1,
name: 'Text field',
isRequired: true,
); // Validation OK
final rule2 = Rule(
textFieldValue2,
name: 'Text field',
); // Validation OK
final groupRule = GroupRule(
[rule1, rule2],
name: 'Group name',
requiredAll: true,
customErrorText: 'Invalid input.',
);
print(groupRule.error);
// output: Invalid input.
}
copied to clipboard
Options
Available options
bool trim;
bool lowerCase;
bool upperCase;
copied to clipboard
trim: bool
Removes any leading and trailing whitespace from the input value
Use rule.value for the trimmed value
import 'package:rules/src/models/rule_options.dart';
void main() {
final rule1 = Rule(
' ',
name: 'Name',
isRequired: true,
options: RuleOptions(
trim: true,
),
);
print(rule1.hasError);
// output: true
print(rule1.value);
// output:
final rule2 = Rule(
' [email protected] ',
name: 'Email',
isEmail: true,
options: RuleOptions(
trim: true,
),
);
print(rule2.hasError);
// output: false
print(rule2.value);
// output: [email protected]
}
copied to clipboard
lowercase: bool
Converts all characters, in the input string, to lower case
Use rule.value for the converted value
import 'package:rules/src/models/rule_options.dart';
void main() {
final rule = Rule(
'ABC',
name: 'Input field',
regex: RegExp('[a-z]'),
options: RuleOptions(
lowerCase: true,
),
);
print(rule.hasError);
// output: false
print(rule.value);
// output: abc
}
copied to clipboard
upperCase: bool
Converts all characters, in the input string, to upper case
Use rule.value for the converted value
import 'package:rules/src/models/rule_options.dart';
void main() {
final rule = Rule(
'xyz',
name: 'Input field',
regex: RegExp('[A-Z]'),
options: RuleOptions(
upperCase: true,
),
);
print(rule.hasError);
// output: false
print(rule.value);
// output: xyz
}
copied to clipboard
Extension
Extend and override the constraints of a group rule using 'copyWith' method.
The child rule will default to the constraint values of the parent unless they are set explicitly in the child.
void main() {
const textFieldValue1 = 'abc';
const textFieldValue2 = '';
final rule1 = Rule(
textFieldValue1,
name: 'Text field',
isRequired: true,
); // Validation OK
final rule2 = Rule(
textFieldValue2,
name: 'Text field',
); // Validation OK
// parent
final groupRule1 = GroupRule(
[rule1, rule2],
name: 'Group name 1',
requiredAll: true,
customErrorText: 'Invalid input for Group 1',
); // Validation FAILED
// child
final groupRule2 = groupRule1.copyWith(
name: 'Group name 2',
requiredAll: false,
customErrorText: 'Invalid input for Group 2',
); // Validation OK
print(groupRule1.error);
// output: Invalid input for Group 1
print(groupRule2.error);
// output: null
}
copied to clipboard
void main() {
const textFieldValue1 = 'abc';
final rule1 = Rule(
textFieldValue1,
name: 'Text field',
isRequired: true,
); // Validation OK
final rule2 = rule1.copyWith(
name: 'Text field',
); // Validation OK
// parent
final groupRule1 = GroupRule(
[rule1, rule2],
name: 'Group name 1',
requiredAll: true,
customErrorText: 'Invalid input for Group 1',
); // Validation OK
// child
final groupRule2 = groupRule1.copyWith(
name: 'Group name 2',
requiredAll: false,
customErrorText: 'Invalid input for Group 2',
); // Validation OK
print(groupRule1.error);
// output: null
print(groupRule2.error);
// output: null
}
copied to clipboard
3. CombinedRule
Manage multiple Rules and GroupRules
Both 'Rule' and/or 'GroupRule' are accepted as inputs.
Errors of both 'Rule' and 'GroupRule', if any, are combined into a list.
Order of appearance of error texts in CombinedRule errorList:
Rule
GroupRule
Basic example
void main() {
const textFieldValue1 = '';
const textFieldValue2 = '[email protected]';
final rule1 = Rule(
textFieldValue1,
name: 'Text field 1',
); // Validation OK
final rule2 = Rule(
textFieldValue2,
name: 'Text field 2',
isEmail: true,
); // Validation OK
final groupRule = GroupRule([rule1, rule2],
name: 'Group name', requiredAll: true); // Validation FAILED
const textFieldValue3 = '';
final rule3 = Rule(
textFieldValue3,
name: 'Text field 3',
isRequired: true,
); // Validation FAILED
final combinedRule = CombinedRule(
rules: [rule3],
groupRules: [groupRule],
);
print(combinedRule.errorList);
// output: ['Text field 3 is required', 'All fields are mandatory in Group name']
print(combinedRule.hasError);
// output: true
if (combinedRule.hasError) {
// some action on error
} else {
// Some action on success
}
}
copied to clipboard
Available fields
List<Rule> rules;
List<GroupRule> groupRules;
copied to clipboard
rules: List<Rule>
void main() {
const textFieldValue1 = '';
const textFieldValue2 = 'abc@xyz';
const textFieldValue3 = '';
final rule1 = Rule(
textFieldValue1,
name: 'Text field 1',
); // Validation OK
final rule2 = Rule(
textFieldValue2,
name: 'Text field 2',
isEmail: true,
); // Validation FAILED
final rule3 = Rule(
textFieldValue3,
name: 'Text field 3',
isRequired: true,
customErrorText: 'Invalid field input',
); // Validation FAILED
final combinedRule = CombinedRule(
rules: [rule1, rule2, rule3],
);
print(combinedRule.errorList);
// output: ['Text field 2 is not a valid email address', 'Invalid field input']
print(combinedRule.hasError);
// output: true
}
copied to clipboard
groupRules: List<GroupRule>
void main() {
const textFieldValue1 = '';
const textFieldValue2 = 'abc@xyz';
final rule1 = Rule(
textFieldValue1,
name: 'Text field 1',
); // Validation OK
final rule2 = Rule(
textFieldValue2,
name: 'Text field 2',
isEmail: true,
); // Validation FAILED
final groupRule1 = GroupRule(
[rule1, rule2],
name: 'Group name',
requiredAll: true,
customErrorText: 'Invalid input',
); // Validation FAILED
final groupRule2 = GroupRule(
[],
name: 'Group name',
); // Validation OK
final combinedRule = CombinedRule(
groupRules: [groupRule1, groupRule2],
);
print(combinedRule.errorList);
// output: ['Invalid input']
print(combinedRule.hasError);
// output: true
}
copied to clipboard
Flutter
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String emailInput;
String phoneInput;
void _handleEmailTextFieldOnChange(String value) {
setState(() {
emailInput = value;
});
}
void _handlePhoneTextFieldOnChange(String value) {
setState(() {
phoneInput = value;
});
}
Rule get emailInputRule {
return Rule(
emailInput,
name: 'Email',
isAlphaSpace: true,
isRequired: true,
customErrorText: 'Invalid Email',
);
}
Rule get phoneInputRule {
return Rule(
phoneInput,
name: 'Phone',
isPhone: true,
isRequired: true,
customErrorText: 'Invalid Phone',
);
}
bool get isContinueBtnEnabled {
final groupRule = GroupRule(
[emailInputRule, phoneInputRule],
name: 'Continue Button',
requiredAll: true,
);
return !groupRule.hasError;
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
TextField(
onChanged: (String value) {
_handleEmailTextFieldOnChange(value);
},
decoration: InputDecoration(
hintText: 'Email address',
errorText: emailInputRule?.error ?? null,
),
),
TextField(
onChanged: (String value) {
_handlePhoneTextFieldOnChange(value);
},
decoration: InputDecoration(
hintText: 'Phone',
errorText: phoneInputRule?.error ?? null,
),
),
if (isContinueBtnEnabled)
FlatButton(
onPressed: () {
// call api
},
child: const Text('Continue'),
)
else
Container()
],
),
),
);
}
}
copied to clipboard
Flutter Mobx
Mobx Store
class UpdateUserStore = _UpdateUserStoreBase
with _$UpdateUserStore;
abstract class _UpdateUserStoreBase with Store {
@observable
String profileName;
@observable
String profileEmail;
@computed
Rules get profileNameInputRule {
final rule = Rules(
profileName,
name: 'Name',
isRequired: false,
isAlphaSpace: true,
customErrorText: 'Invalid name',
);
return rule;
}
@computed
Rules get profileEmailInputRule {
final rule = Rules(
profileEmail,
name: 'Name',
isRequired: false,
isEmail: true,
customErrorText: 'Invalid email',
);
return rule;
}
@computed
bool get isContinueButtonEnabled {
final groupRule = GroupRules(
[profileEmailInputRule, profileNameInputRule],
name: 'Continue Button',
requiredAll: true,
);
return !groupRule.hasError;
}
@action
void setProfileName(String value) {
profileName = value;
}
@action
void setProfileEmail(String value) {
profileEmail = value;
}
}
copied to clipboard
Widget
class _UpdateUserScreen extends State<UpdateUserScreen> {
UpdateUserStore _updateUserStore = UpdateUserStore();
void _handleProfileNameOnChange(String value) {
_updateUserStore.setProfileName(value);
}
void _handleProfileEmailOnChange(String value) {
_updateUserStore.setProfileEmail(value);
}
@override
Widget build(BuildContext context) {
return Container(
width: Display.getWidth(context),
height: Display.getHeight(context),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Observer(
builder: (_) {
final _errorText = _updateUserStore.profileNameInputRule?.error;
return TextField(
onChanged: (String value) {
_handleProfileNameOnChange(value);
},
decoration: InputDecoration(
hintText: 'Name',
errorText: _errorText,
),
);
},
),
Observer(
builder: (_) {
final _errorText = _updateUserStore.profileEmailInputRule?.error;
return TextField(
onChanged: (String value) {
_handleProfileEmailOnChange(value);
},
decoration: InputDecoration(
hintText: 'Email',
errorText: _errorText,
),
);
},
),
Observer(
builder: (_) {
final _isContinueButtonEnabled =
_updateUserStore.isContinueButtonEnabled;
if (!_isContinueButtonEnabled) {
return Container();
}
return FlatButton(
onPressed: () {},
child: const Text('Continue'),
);
},
),
],
),
);
}
}
copied to clipboard
Changelogs #
2.0.0
New feature:
Null safety
Added shouldPass
1.2.0+1
New feature:
Added trim, upperCase and lowerCase rule options
1.1.0
Breaking changes:
regex now expects a RegExp object instead of String
Buy me a coffee #
Help me keep the app FREE and open for all.
Paypal me: paypal.me/ganeshrvel
Contacts #
Please feel free to contact me at [email protected]
About #
Author: Ganesh Rathinavel
License: MIT
Package URL: https://pub.dev/packages/rules
Repo URL: https://github.com/ganeshrvel/pub-rules
Contacts: [email protected]
License #
Rules | Powerful and feature-rich validation library for both Dart and Flutter. MIT License.
Copyright © 2018 - Present Ganesh Rathinavel
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.