Skip to content

feat: add new validators, Flutter form integration, example app, and CI#2

Merged
divyanshub024 merged 4 commits into
mainfrom
dv/new-validators
Mar 27, 2026
Merged

feat: add new validators, Flutter form integration, example app, and CI#2
divyanshub024 merged 4 commits into
mainfrom
dv/new-validators

Conversation

@divyanshub024
Copy link
Copy Markdown
Member

@divyanshub024 divyanshub024 commented Mar 27, 2026

Summary

This PR introduces a major upgrade to flutter_validators, expanding the package with new validators, improved Flutter form integration, enhanced documentation, and full test coverage.

Key Changes

✨ New Validators

Added several commonly used validators to make the package more comprehensive:

  • URL
  • UUID
  • Date
  • Numeric
  • Alpha
  • IP (IPv4 & IPv6)
  • Hex Color
  • Credit Card
  • Length

🧩 Flutter Form Integration

Added a new form_validator.dart utility that provides a Validator class designed for seamless use with Flutter's TextFormField.validator.
Each validator returns a String? Function(String?) closure and supports custom error messages.

Example:

TextFormField(
  decoration: const InputDecoration(labelText: 'Email'),
  validator: Validator.email(errorMessage: 'Enter a valid email'),
)

🧪 Test Coverage

  • Added comprehensive unit tests for all validators
  • Achieved 100% test coverage
  • Fixed minor regex edge cases in existing validators

📦 Example App

Added a complete Flutter example project demonstrating how to use the validators in a real Flutter form.

📚 Documentation Improvements

  • Completely refreshed README with a new banner and structured documentation
  • Added validator reference tables
  • Improved usage examples

⚙️ CI Setup

Added a GitHub Actions workflow to:

  • Install dependencies
  • Run static analysis
  • Execute tests automatically on push and pull requests

Miscellaneous

  • Updated copyright year in LICENSE
  • Updated CHANGELOG for the new release
  • Renamed a test file for consistency

Summary by CodeRabbit

  • New Features

    • Added multiple new validators (alpha, credit card, date, hex color, IP, length, numeric, URL, UUID) and a Form Validator API for Flutter forms with customizable error messages.
    • Added a full example app showing validator usage.
  • Documentation

    • Revamped README with Quick Start, usage examples, and full validator reference.
    • Updated changelog for v1.1.0.
  • Tests & CI

    • New CI workflow to run static analysis and tests; comprehensive validator test suite added.
  • License

    • Updated copyright year in LICENSE.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 27, 2026

📝 Walkthrough

Walkthrough

Adds v1.1.0: a new Flutter Validator form helper, nine new validator modules and exports, comprehensive tests, a multi-platform example app scaffold (Android/iOS/macos/linux/windows/web), updated docs/CHANGELOG/README, LICENSE year bump, and a GitHub Actions workflow for analyze+tests.

Changes

Cohort / File(s) Summary
CI Workflow
​.github/workflows/test.yml
Added GitHub Actions workflow "Test and Analyze" running checkout, Flutter (stable) install, dart pub get, dart analyze, and dart test on push/pull_request to main.
Project Metadata
pubspec.yaml, LICENSE
Bumped package version to 1.1.0; updated MIT copyright year to 2026.
Documentation & Changelog
CHANGELOG.md, README.md
Added v1.1.0 changelog entry and substantially rewrote README with Quick Start, String extensions, top-level functions, Flutter Form integration, and full validator reference.
Library Exports
lib/flutter_validators.dart
Expanded public exports to include new validator modules and form_validator.dart.
Form Validator API
lib/form_validator.dart
Added public Validator class exposing many static factory methods that return String? Function(String?) closures for use as TextFormField.validator.
New Validators (core)
lib/validators/...
lib/validators/alpha.dart, credit_card.dart, date.dart, hex_color.dart, ip.dart, length.dart, numeric.dart, url.dart, uuid.dart
Added nine validator modules providing top-level boolean functions and String extensions (e.g., isAlpha, isCreditCard, isDate, isHexColor, isIP, isLength, isNumeric, isURL, isUUID) with validation logic (regexes, parsing, Luhn).
Validator refinements
lib/validators/base32.dart, lib/validators/base58.dart
Tightened Base32/Base58 validation (reject empty strings, stricter padding/charset rules).
Tests
test/*, test/validators/*
Added/updated many unit tests including test/form_validator_test.dart, per-validator tests, test/flutter_validators_test.dart, and example/test/widget_test.dart.
Example app — common files
example/pubspec.yaml, example/lib/main.dart, example/README.md, example/analysis_options.yaml, example/.metadata, example/*.gitignore
Added a full Flutter example project with main.dart demonstrating Validator usage, widget tests, lints, and supporting metadata/ignore files.
Example app — Android
example/android/**
Added complete Android Gradle KTS build scripts, manifests (main/debug/profile), MainActivity.kt, drawables, styles/themes, Gradle wrapper and properties, and project-level build customization.
Example app — iOS
example/ios/**
Added Xcode project/workspace, Info.plist, AppDelegate/SceneDelegate, storyboards, asset catalogs, entitlements, and scheme/workspace settings.
Example app — macOS
example/macos/**
Added macOS Xcode project, AppDelegate/MainFlutterWindow, XIB (MainMenu), asset catalogs, entitlements, and configs.
Example app — Linux
example/linux/**
Added CMake build, Flutter integration CMake files, GTK runner (my_application.*, main.cc), generated plugin stubs, and install/bundle rules.
Example app — Windows
example/windows/**
Added Windows CMake files, Win32 runner code (main.cpp, win32_window.*, flutter_window.*, utils.*), resource script/header, manifest, and plugin registrant stubs.
Example app — Web
example/web/{index.html,manifest.json}
Added web entrypoint HTML and web app manifest for the example.
Large generated/project files
example/ios/Runner.xcodeproj/project.pbxproj, example/macos/Runner.xcodeproj/project.pbxproj
Added large Xcode project files for iOS/macOS (project definitions, targets, build phases, and settings).

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Poem

🐇 I nibble regex, hop with glee,

Validators joined — a perfect spree,
Forms and examples, tests in tow,
Across each platform we now go! ✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dv/new-validators

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

🧹 Nitpick comments (9)
example/linux/runner/my_application.cc (2)

104-116: Remove stale placeholder comments in lifecycle hooks.

The commented self lines and generic placeholder text in startup/shutdown add noise without conveying actionable behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/linux/runner/my_application.cc` around lines 104 - 116, Remove the
stale placeholder comments in the lifecycle hooks: delete the commented
"MyApplication* self = MY_APPLICATION(object);" lines and the generic "Perform
any actions required..." comments inside my_application_startup and
my_application_shutdown; if specific startup/shutdown work is required, replace
these placeholders with the actual calls or TODO comments referencing
MyApplication, otherwise leave the functions clean and only call the parent
handlers via
G_APPLICATION_CLASS(my_application_parent_class)->startup(application) and
G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application)
respectively.

87-88: Clear previous duplicated argv before reassigning.

Line 87 overwrites self->dart_entrypoint_arguments directly. Clearing prior storage first avoids leaking memory if this path is hit more than once.

Suggested patch
 static gboolean my_application_local_command_line(GApplication* application,
                                                   gchar*** arguments,
                                                   int* exit_status) {
   MyApplication* self = MY_APPLICATION(application);
   // Strip out the first argument as it is the binary name.
+  g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
   self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/linux/runner/my_application.cc` around lines 87 - 88, The code
overwrites self->dart_entrypoint_arguments with g_strdupv(*arguments + 1)
without freeing any previous value, leaking memory if executed multiple times;
before assigning, check if self->dart_entrypoint_arguments is non-null and free
the previous argv using g_strfreev(self->dart_entrypoint_arguments), then set
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); keep the same
allocation call (g_strdupv) and do this cleanup in the same scope where the
assignment occurs in my_application.cc.
test/flutter_validators_test.dart (1)

1-1: Consider removing or repurposing this empty test file.

This file contains only an empty main() function. If tests have been moved to more specific files (e.g., test/form_validator_test.dart, test/validators/*_test.dart), consider removing this file to avoid confusion about where tests live.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/flutter_validators_test.dart` at line 1, This file contains only an
empty main() test entrypoint; either delete this test file or repurpose it by
adding meaningful tests (e.g., move or consolidate tests from
form_validator_test.dart or validators/*_test.dart into this file) so it is not
a no-op; locate the empty main() in flutter_validators_test.dart and either
remove the file or replace the empty main() with the appropriate test group(s)
that exercise your validator functions.
test/validators/length_test.dart (1)

6-16: Consider adding edge-case coverage.

The current tests cover basic valid and invalid scenarios. Consider adding tests for edge cases such as empty strings, single-character strings, boundary conditions with equal min/max, and potentially invalid inputs (e.g., min > max, negative values) to ensure robustness.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/validators/length_test.dart` around lines 6 - 16, Add tests in
test/validators/length_test.dart around the existing isLength cases to cover
edge cases: assert behavior for empty string (''.isLength(0), ''.isLength(0,0)),
single-character string ('a'.isLength(1)), boundary conditions where min == max
(e.g., 'a'.isLength(1,1) and 'ab'.isLength(2,2)), invalid constraint cases such
as min > max (e.g., expect('a'.isLength(2,1), isFalse or the function's defined
behavior)), and negative values for min/max to verify handling; reference the
existing test group names and the isLength(String) extension/function to add
these assertions so behavior is fully specified.
test/validators/base32_test.dart (1)

12-17: Nice coverage; consider one padding-placement edge case.

Line 12–17 could also assert that malformed = placement is rejected (not just length/charset), to guard regex regressions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/validators/base32_test.dart` around lines 12 - 17, In the test block
named 'Invalid base32' add assertions that strings with incorrectly placed or
malformed padding are rejected by the isBase32 check; for example add expects
like expect('JB=SWY3DPEHPK3PX'.isBase32, isFalse),
expect('=JBSWY3DPEHPK3PX'.isBase32, isFalse) and
expect('JBSWY3DPEHPK3PX=='.isBase32, isFalse) so the isBase32 property is
verified to reject '=' appearing in the middle, leading padding, or invalid
padding counts.
example/android/build.gradle.kts (1)

14-20: Consider merging the two subprojects blocks.

The two separate subprojects blocks can be combined into one for improved readability.

♻️ Suggested refactor
 subprojects {
     val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
     project.layout.buildDirectory.value(newSubprojectBuildDir)
-}
-subprojects {
     project.evaluationDependsOn(":app")
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/android/build.gradle.kts` around lines 14 - 20, Merge the two
subprojects blocks into a single block to improve readability: move the build
directory configuration (val newSubprojectBuildDir: Directory =
newBuildDir.dir(project.name) and
project.layout.buildDirectory.value(newSubprojectBuildDir)) and the evaluation
dependency call (project.evaluationDependsOn(":app")) into one subprojects { ...
} scope so both configurations are applied together.
test/form_validator_test.dart (1)

22-27: Consider adding a test for null input to the length validator.

The required and email validators both test null handling, but the length validator tests don't verify behavior when null is passed. This could help catch unexpected exceptions or ensure consistent null-handling across validators.

🧪 Suggested additional test case
     test('Length validator', () {
       final len = Validator.length(3, max: 5, errorMessage: 'Bad len');
       expect(len('abc'), isNull);
       expect(len('ab'), equals('Bad len'));
       expect(len('abcdef'), equals('Bad len'));
+      expect(len(null), isNull); // or equals('Bad len') depending on expected behavior
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/form_validator_test.dart` around lines 22 - 27, Add a test that verifies
Validator.length handles null input without throwing and returns the expected
result (likely an error message or null depending on intended behavior); update
the 'Length validator' test block (the test with final len = Validator.length(3,
max: 5, errorMessage: 'Bad len')) to include an assertion for len(null) so
null-handling for Validator.length is covered consistently with required and
email validators.
test/validators/uuid_test.dart (1)

6-11: Move the invalid UUID test case to the "Invalid UUIDs" group.

Line 9 tests an invalid UUID (contains non-hex character 't') but is placed inside the "Valid UUIDs" test. This reduces test clarity and makes failures harder to diagnose.

♻️ Suggested refactor
     test('Valid UUIDs', () {
       expect('123e4567-e89b-12d3-a456-426614174000'.isUUID, isTrue);
       expect('550e8400-e29b-41d4-a716-446655440000'.isUUID, isTrue);
-      expect('8bA4A1C9-5e19-4F5t-b5a8-44243b1E2D4A'.isUUID, isFalse); // invalid hex char 't'
       expect('8bA4A1C9-5e19-4F5b-b5a8-44243b1E2D4A'.isUUID, isTrue);
     });

     test('Invalid UUIDs', () {
       expect('invalid-uuid'.isUUID, isFalse);
       expect('123e4567-e89b-12d3-a456-42661417400'.isUUID, isFalse); // Too short
+      expect('8bA4A1C9-5e19-4F5t-b5a8-44243b1E2D4A'.isUUID, isFalse); // Invalid hex char 't'
       expect(''.isUUID, isFalse);
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/validators/uuid_test.dart` around lines 6 - 11, The test case asserting
that '8bA4A1C9-5e19-4F5t-b5a8-44243b1E2D4A'.isUUID isFalse is currently inside
the "Valid UUIDs" test; move that expect line into the "Invalid UUIDs" group so
the invalid-case string (contains non-hex 't') is tested alongside other invalid
samples, keeping the "Valid UUIDs" group reserved for only isTrue expectations
and the "Invalid UUIDs" group for isFalse expectations.
lib/validators/length.dart (1)

22-24: Consider validating min and max parameters.

The function doesn't validate that min >= 0 or that max >= min when both are provided. Invalid parameters like isLength("abc", -1) or isLength("abc", 5, 3) will produce potentially confusing results rather than clear errors.

🛡️ Suggested defensive validation
 bool _isLength(String str, int min, [int? max]) {
+  assert(min >= 0, 'min must be non-negative');
+  assert(max == null || max >= min, 'max must be >= min');
   return str.length >= min && (max == null || str.length <= max);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/validators/length.dart` around lines 22 - 24, The _isLength function
should defensively validate its parameters: ensure min is >= 0 and if max is
provided ensure max >= min; if not, throw an ArgumentError (or RangeError) with
a clear message. Update the _isLength(String str, int min, [int? max]) entry
point to perform these checks before using str.length so invalid calls like
isLength("a",-1) or isLength("a",5,3) fail fast and clearly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/test.yml:
- Line 13: Update the GitHub Actions step using the deprecated
actions/checkout@v3 to actions/checkout@v4: locate the workflow step referencing
"uses: actions/checkout@v3" in the test.yml and change the version tag to "@v4"
so the job uses the latest checkout action (no other code changes required
unless you pin a full SHA or adjust inputs for v4-specific options).

In `@example/ios/Runner.xcodeproj/project.pbxproj`:
- Line 369: The project file currently hard-codes the Apple developer team via
DEVELOPMENT_TEAM = W2B7PMH9SQ; in the Xcode project, which prevents external
contributors/CI from building; remove those DEVELOPMENT_TEAM assignments from
the committed project.pbxproj (the occurrences you saw) or instead move them
into a local/user-specific xcconfig that is not committed (and reference that
xcconfig in the project), and update the project to use the unspecified/blank
team setting so builds default to the local developer/CI team. Ensure you remove
all instances of the DEVELOPMENT_TEAM = W2B7PMH9SQ; entries (the three
occurrences flagged) or replace them with an xcconfig mechanism and add that
xcconfig to .gitignore.

In `@example/pubspec.yaml`:
- Around line 21-23: The example's pubspec "environment" block currently sets
"sdk: ^3.11.3" which conflicts with the root package's SDK floor of "^3.7.0";
update the example's pubspec.yaml to align these constraints by either lowering
the example "sdk" floor to "^3.7.0" (if compatible) or raising the root package
SDK floor to "^3.11.3" so both match, or add a brief comment in the example's
pubspec explaining why the higher floor is required; look for the "environment"
-> "sdk: ^3.11.3" entry to change.

In `@example/test/widget_test.dart`:
- Around line 14-28: Replace the failing counter test inside the
testWidgets('Counter increments smoke test', ...) block with a form-validator
test: pump the MyApp widget (MyApp), locate the TextFormField widgets (use their
Keys or hintText/labelText for email, url, credit card, numeric fields), enter
invalid values via tester.enterText for each field, tap the form submit/validate
button via tester.tap (or call tester.testTextInput.receiveAction if form
validates on field), await tester.pump(), and assert that the appropriate
validation error text widgets appear (expect(find.text('Enter a valid email'),
findsOneWidget) etc. Then enter valid inputs, tap submit again, pump, and assert
that the error texts are gone and success state (e.g., success message or
absence of error widgets) is present; replace references to Icons.add and
find.text('0'/'1') with the form field interactions above.

In `@example/windows/runner/utils.cpp`:
- Around line 13-18: The condition checks for freopen_s are inverted and the
stderr duplication uses the wrong file pointer; change the logic so _dup2 is
called when freopen_s returns 0 (success) and for the stderr branch use
_fileno(stderr) when duplicating: update the two if(...) checks that call
freopen_s(&unused, "CONOUT$", "w", stdout/stderr) to test for == 0 and call
_dup2(_fileno(stdout), 1) for stdout and _dup2(_fileno(stderr), 2) for stderr
(symbols: freopen_s, _dup2, _fileno, stdout, stderr, unused).
- Around line 48-56: The code subtracts 1 from the unsigned result of
WideCharToMultiByte into target_length, which underflows when
WideCharToMultiByte returns 0; change the logic in the WideCharToMultiByte call
handling (around target_length, utf16_string, and utf8_string) to first capture
the return value into a signed/checked variable (e.g., int required =
::WideCharToMultiByte(...)); if required <= 0 return empty string; otherwise
compute target_length = required - 1 safely (or use size_t after checking), then
proceed to resize utf8_string using that safe length; update any checks that
relied on target_length == 0 accordingly.

In `@example/windows/runner/win32_window.cpp`:
- Around line 118-121: Destructor Win32Window::~Win32Window currently calls
Destroy(), which can re-enter Destroy() via WM_DESTROY and cause OnDestroy()
(e.g., FlutterWindow::OnDestroy()) to run multiple times; make the destroy path
idempotent by adding a guard (e.g., bool destroying_ or destroyed_) as a member,
check-and-set it at the start of Win32Window::Destroy() and in the destructor to
ensure Destroy() and OnDestroy() execute only once, or alternatively split the
one-time teardown (OnDestroy()) into a separate method invoked only when the
guard indicates first-time destruction; update all call sites
(Win32Window::~Win32Window, Destroy(), WM_DESTROY handler, and OnDestroy()) to
use the guard so double-teardown cannot happen.

In `@lib/form_validator.dart`:
- Around line 114-117: In _build, optional validators treat whitespace-only
strings as non-empty; change the emptiness check to treat strings with only
whitespace as empty by trimming the input before checking: in the closure
returned by _build (function _build), replace the current if (value == null ||
value.isEmpty) check with a check that uses value.trim().isEmpty so that
whitespace-only input returns null (letting required handle emptiness) and only
non-blank strings are passed to the provided test function.

In `@lib/validators/base32.dart`:
- Around line 40-42: The current validator rejects valid unpadded Base32 and
allows arbitrary padding; remove the strict length % 8 check and instead
validate padding correctly: use a tighter regex like /^[A-Z2-7]+={0,6}$/ to
ensure only trailing '=' are allowed, then if there are padding characters
enforce that the total length is a multiple of 8 and that the pad count is one
of the valid values (1, 3, 4, or 6); update the function that returns the
boolean (the code using str.length % 8 and RegExp(r'^[A-Z2-7]+=*$')) to
implement these rules so unpadded inputs pass and invalid padding layouts are
rejected.

In `@lib/validators/ip.dart`:
- Around line 31-35: The _isIP function currently treats any unsupported numeric
version as a signal to fallback to IPv4/IPv6 checks; change its logic so only
null/undefined version triggers the fallback, while explicit unsupported
versions return false. Concretely, in _isIP (and any wrapper isIP if present)
keep the checks for version == 4 and version == 6, perform the IPv4/IPv6 OR only
when version is null (or omitted), and return false for any other explicit
version values to avoid incorrectly accepting e.g. version == 5.

In `@lib/validators/numeric.dart`:
- Around line 23-25: The _isNumeric(String str) validator should reject
non-finite values; change its logic to call double.tryParse(str), store the
result in a local variable (e.g., var v), and return true only when v != null &&
v.isFinite so that "NaN" and "Infinity" are treated as invalid inputs.

In `@lib/validators/uuid.dart`:
- Around line 1-3: The UUID validator currently accepts any hex nibble for the
version and variant; update the validation regex used in the UUID validator to
strictly require the version nibble to be one of 1,3,4,5 and the variant nibble
to be one of 8,9,A,B (case-insensitive). Locate the UUID validation regex in the
validator function (the regex that matches the third and fourth hyphen-separated
groups), change the version group to [1345] and the variant group to [89ABab]
(or use case-insensitive flag), keep the overall 8-4-4-4-12 structure and
anchors, and add/adjust unit tests to cover valid versions 1/3/4/5 and invalid
versions/variants.

In `@README.md`:
- Around line 21-26: The README currently claims "20+ validators" but the
validators table documents only 19 entries; either update the hero copy to "19
validators" (and adjust the repeated "20+ validators" phrases) or add the
missing validator row(s) to the validators table so the count is accurate;
inspect the library's exported validator functions (and the built-in Validator
class/String extensions referenced in the README) to determine the true number,
then update the hero text and any other occurrences of "20+ validators" to match
that authoritative count or expand the table to include the missing
validator(s).
- Line 16: The license badge's anchor (<a href="...">) currently links to the
badge image URL; update the anchor's href to point to the repository LICENSE
file (for example "LICENSE" or "./LICENSE" or the absolute LICENSE URL) so
clicking the badge opens the actual license document instead of the image;
locate the anchor element that wraps the badge image and replace its href value
accordingly.

In `@test/validators/date_test.dart`:
- Around line 12-16: The assertion for '2023-13-01' in the "Invalid Dates" test
is inconsistent with its placement; inspect the intended behavior of the isDate
validator and either (a) if isDate should validate semantic calendar dates,
change the expectation in the 'Invalid Dates' test from isTrue to isFalse for
'2023-13-01', or (b) if isDate intentionally accepts DateTime.tryParse-style
rollover, move the '2023-13-01' expect(...) line out of the "Invalid Dates" test
into the "Valid Dates" test block so the test name matches the assertion; update
the test referring to the 'isDate' property accordingly.

In `@test/validators/int_test.dart`:
- Around line 13-18: The test named 'Invalid integers' contains a contradictory
expectation: it asserts '123 '.isInt as isTrue inside an invalid-case block;
update the assertion to reflect the test intent by either moving the '123
'.isInt expectation into the valid integers test or flipping it to isFalse in
this test. Locate the test function labeled "Invalid integers" in
test/validators/int_test.dart and adjust the expect call for the '123 ' input
(the isInt check) so the expectation matches the test purpose.

---

Nitpick comments:
In `@example/android/build.gradle.kts`:
- Around line 14-20: Merge the two subprojects blocks into a single block to
improve readability: move the build directory configuration (val
newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) and
project.layout.buildDirectory.value(newSubprojectBuildDir)) and the evaluation
dependency call (project.evaluationDependsOn(":app")) into one subprojects { ...
} scope so both configurations are applied together.

In `@example/linux/runner/my_application.cc`:
- Around line 104-116: Remove the stale placeholder comments in the lifecycle
hooks: delete the commented "MyApplication* self = MY_APPLICATION(object);"
lines and the generic "Perform any actions required..." comments inside
my_application_startup and my_application_shutdown; if specific startup/shutdown
work is required, replace these placeholders with the actual calls or TODO
comments referencing MyApplication, otherwise leave the functions clean and only
call the parent handlers via
G_APPLICATION_CLASS(my_application_parent_class)->startup(application) and
G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application)
respectively.
- Around line 87-88: The code overwrites self->dart_entrypoint_arguments with
g_strdupv(*arguments + 1) without freeing any previous value, leaking memory if
executed multiple times; before assigning, check if
self->dart_entrypoint_arguments is non-null and free the previous argv using
g_strfreev(self->dart_entrypoint_arguments), then set
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); keep the same
allocation call (g_strdupv) and do this cleanup in the same scope where the
assignment occurs in my_application.cc.

In `@lib/validators/length.dart`:
- Around line 22-24: The _isLength function should defensively validate its
parameters: ensure min is >= 0 and if max is provided ensure max >= min; if not,
throw an ArgumentError (or RangeError) with a clear message. Update the
_isLength(String str, int min, [int? max]) entry point to perform these checks
before using str.length so invalid calls like isLength("a",-1) or
isLength("a",5,3) fail fast and clearly.

In `@test/flutter_validators_test.dart`:
- Line 1: This file contains only an empty main() test entrypoint; either delete
this test file or repurpose it by adding meaningful tests (e.g., move or
consolidate tests from form_validator_test.dart or validators/*_test.dart into
this file) so it is not a no-op; locate the empty main() in
flutter_validators_test.dart and either remove the file or replace the empty
main() with the appropriate test group(s) that exercise your validator
functions.

In `@test/form_validator_test.dart`:
- Around line 22-27: Add a test that verifies Validator.length handles null
input without throwing and returns the expected result (likely an error message
or null depending on intended behavior); update the 'Length validator' test
block (the test with final len = Validator.length(3, max: 5, errorMessage: 'Bad
len')) to include an assertion for len(null) so null-handling for
Validator.length is covered consistently with required and email validators.

In `@test/validators/base32_test.dart`:
- Around line 12-17: In the test block named 'Invalid base32' add assertions
that strings with incorrectly placed or malformed padding are rejected by the
isBase32 check; for example add expects like expect('JB=SWY3DPEHPK3PX'.isBase32,
isFalse), expect('=JBSWY3DPEHPK3PX'.isBase32, isFalse) and
expect('JBSWY3DPEHPK3PX=='.isBase32, isFalse) so the isBase32 property is
verified to reject '=' appearing in the middle, leading padding, or invalid
padding counts.

In `@test/validators/length_test.dart`:
- Around line 6-16: Add tests in test/validators/length_test.dart around the
existing isLength cases to cover edge cases: assert behavior for empty string
(''.isLength(0), ''.isLength(0,0)), single-character string ('a'.isLength(1)),
boundary conditions where min == max (e.g., 'a'.isLength(1,1) and
'ab'.isLength(2,2)), invalid constraint cases such as min > max (e.g.,
expect('a'.isLength(2,1), isFalse or the function's defined behavior)), and
negative values for min/max to verify handling; reference the existing test
group names and the isLength(String) extension/function to add these assertions
so behavior is fully specified.

In `@test/validators/uuid_test.dart`:
- Around line 6-11: The test case asserting that
'8bA4A1C9-5e19-4F5t-b5a8-44243b1E2D4A'.isUUID isFalse is currently inside the
"Valid UUIDs" test; move that expect line into the "Invalid UUIDs" group so the
invalid-case string (contains non-hex 't') is tested alongside other invalid
samples, keeping the "Valid UUIDs" group reserved for only isTrue expectations
and the "Invalid UUIDs" group for isFalse expectations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4cf69597-b587-4e51-b12d-0f583732fad1

📥 Commits

Reviewing files that changed from the base of the PR and between 4eaea88 and 745fb7f.

⛔ Files ignored due to path filters (38)
  • assets/banner.png is excluded by !**/*.png
  • example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png is excluded by !**/*.png
  • example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png is excluded by !**/*.png
  • example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png is excluded by !**/*.png
  • example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png is excluded by !**/*.png
  • example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png is excluded by !**/*.png
  • example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png is excluded by !**/*.png
  • example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png is excluded by !**/*.png
  • example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png is excluded by !**/*.png
  • example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png is excluded by !**/*.png
  • example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png is excluded by !**/*.png
  • example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png is excluded by !**/*.png
  • example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png is excluded by !**/*.png
  • example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png is excluded by !**/*.png
  • example/pubspec.lock is excluded by !**/*.lock
  • example/web/favicon.png is excluded by !**/*.png
  • example/web/icons/Icon-192.png is excluded by !**/*.png
  • example/web/icons/Icon-512.png is excluded by !**/*.png
  • example/web/icons/Icon-maskable-192.png is excluded by !**/*.png
  • example/web/icons/Icon-maskable-512.png is excluded by !**/*.png
  • example/windows/runner/resources/app_icon.ico is excluded by !**/*.ico
📒 Files selected for processing (130)
  • .github/workflows/test.yml
  • CHANGELOG.md
  • LICENSE
  • README.md
  • example/.gitignore
  • example/.metadata
  • example/README.md
  • example/analysis_options.yaml
  • example/android/.gitignore
  • example/android/app/build.gradle.kts
  • example/android/app/src/debug/AndroidManifest.xml
  • example/android/app/src/main/AndroidManifest.xml
  • example/android/app/src/main/kotlin/com/example/example/MainActivity.kt
  • example/android/app/src/main/res/drawable-v21/launch_background.xml
  • example/android/app/src/main/res/drawable/launch_background.xml
  • example/android/app/src/main/res/values-night/styles.xml
  • example/android/app/src/main/res/values/styles.xml
  • example/android/app/src/profile/AndroidManifest.xml
  • example/android/build.gradle.kts
  • example/android/gradle.properties
  • example/android/gradle/wrapper/gradle-wrapper.properties
  • example/android/settings.gradle.kts
  • example/ios/.gitignore
  • example/ios/Flutter/AppFrameworkInfo.plist
  • example/ios/Flutter/Debug.xcconfig
  • example/ios/Flutter/Release.xcconfig
  • example/ios/Runner.xcodeproj/project.pbxproj
  • example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  • example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  • example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  • example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
  • example/ios/Runner.xcworkspace/contents.xcworkspacedata
  • example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  • example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  • example/ios/Runner/AppDelegate.swift
  • example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
  • example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
  • example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
  • example/ios/Runner/Base.lproj/LaunchScreen.storyboard
  • example/ios/Runner/Base.lproj/Main.storyboard
  • example/ios/Runner/Info.plist
  • example/ios/Runner/Runner-Bridging-Header.h
  • example/ios/Runner/SceneDelegate.swift
  • example/ios/RunnerTests/RunnerTests.swift
  • example/lib/main.dart
  • example/linux/.gitignore
  • example/linux/CMakeLists.txt
  • example/linux/flutter/CMakeLists.txt
  • example/linux/flutter/generated_plugin_registrant.cc
  • example/linux/flutter/generated_plugin_registrant.h
  • example/linux/flutter/generated_plugins.cmake
  • example/linux/runner/CMakeLists.txt
  • example/linux/runner/main.cc
  • example/linux/runner/my_application.cc
  • example/linux/runner/my_application.h
  • example/macos/.gitignore
  • example/macos/Flutter/Flutter-Debug.xcconfig
  • example/macos/Flutter/Flutter-Release.xcconfig
  • example/macos/Flutter/GeneratedPluginRegistrant.swift
  • example/macos/Runner.xcodeproj/project.pbxproj
  • example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  • example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
  • example/macos/Runner.xcworkspace/contents.xcworkspacedata
  • example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  • example/macos/Runner/AppDelegate.swift
  • example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
  • example/macos/Runner/Base.lproj/MainMenu.xib
  • example/macos/Runner/Configs/AppInfo.xcconfig
  • example/macos/Runner/Configs/Debug.xcconfig
  • example/macos/Runner/Configs/Release.xcconfig
  • example/macos/Runner/Configs/Warnings.xcconfig
  • example/macos/Runner/DebugProfile.entitlements
  • example/macos/Runner/Info.plist
  • example/macos/Runner/MainFlutterWindow.swift
  • example/macos/Runner/Release.entitlements
  • example/macos/RunnerTests/RunnerTests.swift
  • example/pubspec.yaml
  • example/test/widget_test.dart
  • example/web/index.html
  • example/web/manifest.json
  • example/windows/.gitignore
  • example/windows/CMakeLists.txt
  • example/windows/flutter/CMakeLists.txt
  • example/windows/flutter/generated_plugin_registrant.cc
  • example/windows/flutter/generated_plugin_registrant.h
  • example/windows/flutter/generated_plugins.cmake
  • example/windows/runner/CMakeLists.txt
  • example/windows/runner/Runner.rc
  • example/windows/runner/flutter_window.cpp
  • example/windows/runner/flutter_window.h
  • example/windows/runner/main.cpp
  • example/windows/runner/resource.h
  • example/windows/runner/runner.exe.manifest
  • example/windows/runner/utils.cpp
  • example/windows/runner/utils.h
  • example/windows/runner/win32_window.cpp
  • example/windows/runner/win32_window.h
  • lib/flutter_validators.dart
  • lib/form_validator.dart
  • lib/validators/alpha.dart
  • lib/validators/base32.dart
  • lib/validators/base58.dart
  • lib/validators/credit_card.dart
  • lib/validators/date.dart
  • lib/validators/hex_color.dart
  • lib/validators/ip.dart
  • lib/validators/length.dart
  • lib/validators/numeric.dart
  • lib/validators/url.dart
  • lib/validators/uuid.dart
  • pubspec.yaml
  • test/flutter_validators_test.dart
  • test/form_validator_test.dart
  • test/validators/alpha_test.dart
  • test/validators/ascii_test.dart
  • test/validators/base32_test.dart
  • test/validators/base58_test.dart
  • test/validators/boolean_test.dart
  • test/validators/credit_card_test.dart
  • test/validators/date_test.dart
  • test/validators/email_test.dart
  • test/validators/equals_test.dart
  • test/validators/hex_color_test.dart
  • test/validators/int_test.dart
  • test/validators/ip_test.dart
  • test/validators/length_test.dart
  • test/validators/numeric_test.dart
  • test/validators/phone_test.dart
  • test/validators/url_test.dart
  • test/validators/uuid_test.dart

Comment thread .github/workflows/test.yml Outdated
Comment thread example/ios/Runner.xcodeproj/project.pbxproj Outdated
Comment thread example/pubspec.yaml
Comment thread example/test/widget_test.dart Outdated
Comment thread example/windows/runner/utils.cpp
Comment thread lib/validators/uuid.dart
Comment thread README.md Outdated
Comment thread README.md
Comment thread test/validators/date_test.dart
Comment thread test/validators/int_test.dart
…nsistency, and refine numeric validation to include `isFinite` check.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
README.md (1)

21-26: ⚠️ Potential issue | 🟡 Minor

Validator count still claims "20+" but the table documents 19.

Lines 21 and 26 state "20+ validators," but the reference table (lines 105–126) lists 19 validators. Update the count to "19 validators" or add the missing validator(s) to reach 20+.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 21 - 26, The README's header text saying "20+
validators" is inconsistent with the validators table which lists 19 items;
either change the header phrase "20+ validators" to "19 validators" (and update
any other occurrences of "20+" in the README) or add the missing validator entry
to the table so the total is truly 20+; locate the header string "20+
validators" and the validators table block and update them together (and update
any related badge/summary lines) so the documented count matches the actual
list.
🧹 Nitpick comments (1)
example/test/widget_test.dart (1)

18-22: Consider using more robust field selectors instead of positional indexing.

Using find.byType(TextFormField).at(n) is fragile—if the field order changes in the UI, this test would silently test the wrong fields. Finding fields by their label text or using Key widgets would make the test more resilient to layout changes.

♻️ Example: find fields by label text
-    final emailField = find.byType(TextFormField).at(0);
-    final urlField = find.byType(TextFormField).at(1);
-    final creditCardField = find.byType(TextFormField).at(2);
-    final numericField = find.byType(TextFormField).at(3);
+    final emailField = find.widgetWithText(TextFormField, 'Email').first;
+    final urlField = find.widgetWithText(TextFormField, 'URL').first;
+    final creditCardField = find.widgetWithText(TextFormField, 'Credit Card').first;
+    final numericField = find.widgetWithText(TextFormField, 'Age (Numeric)').first;

Alternatively, assign Key values to each TextFormField in main.dart and use find.byKey(...).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/test/widget_test.dart` around lines 18 - 22, The test is using
fragile positional selectors (find.byType(TextFormField).at(n)) for emailField,
urlField, creditCardField, and numericField; update the test to locate fields
more robustly by either assigning Keys to each TextFormField in the widget under
test (and using find.byKey for emailField, urlField, creditCardField,
numericField) or by using semantic/label-based selectors such as
find.widgetWithText(TextFormField, '<label>') / find.text('<label>') to target
each field by its visible label instead of index so reordering of inputs won't
break the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@README.md`:
- Around line 21-26: The README's header text saying "20+ validators" is
inconsistent with the validators table which lists 19 items; either change the
header phrase "20+ validators" to "19 validators" (and update any other
occurrences of "20+" in the README) or add the missing validator entry to the
table so the total is truly 20+; locate the header string "20+ validators" and
the validators table block and update them together (and update any related
badge/summary lines) so the documented count matches the actual list.

---

Nitpick comments:
In `@example/test/widget_test.dart`:
- Around line 18-22: The test is using fragile positional selectors
(find.byType(TextFormField).at(n)) for emailField, urlField, creditCardField,
and numericField; update the test to locate fields more robustly by either
assigning Keys to each TextFormField in the widget under test (and using
find.byKey for emailField, urlField, creditCardField, numericField) or by using
semantic/label-based selectors such as find.widgetWithText(TextFormField,
'<label>') / find.text('<label>') to target each field by its visible label
instead of index so reordering of inputs won't break the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6ca488a3-c4d3-4dd1-916c-785c7d842fa1

📥 Commits

Reviewing files that changed from the base of the PR and between 745fb7f and 718ff0a.

⛔ Files ignored due to path filters (1)
  • example/pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • .github/workflows/test.yml
  • README.md
  • example/ios/Runner.xcodeproj/project.pbxproj
  • example/pubspec.yaml
  • example/test/widget_test.dart
  • lib/form_validator.dart
  • lib/validators/base32.dart
  • lib/validators/ip.dart
  • lib/validators/numeric.dart
  • lib/validators/uuid.dart
  • test/validators/base32_test.dart
  • test/validators/date_test.dart
  • test/validators/int_test.dart
  • test/validators/uuid_test.dart
✅ Files skipped from review due to trivial changes (8)
  • .github/workflows/test.yml
  • lib/validators/base32.dart
  • example/pubspec.yaml
  • test/validators/int_test.dart
  • test/validators/date_test.dart
  • lib/validators/numeric.dart
  • test/validators/uuid_test.dart
  • example/ios/Runner.xcodeproj/project.pbxproj
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/validators/base32_test.dart
  • lib/validators/uuid.dart
  • lib/form_validator.dart

@divyanshub024 divyanshub024 merged commit 2805822 into main Mar 27, 2026
2 checks passed
@divyanshub024 divyanshub024 deleted the dv/new-validators branch May 15, 2026 09:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant