Skip to content

Replace intl_phone_field with intl_country_data for accurate phone validation#6063

Merged
mdmohsin7 merged 6 commits into
mainfrom
fix/replace-intl-phone-field-with-intl-country-data
Mar 26, 2026
Merged

Replace intl_phone_field with intl_country_data for accurate phone validation#6063
mdmohsin7 merged 6 commits into
mainfrom
fix/replace-intl-phone-field-with-intl-country-data

Conversation

@mdmohsin7
Copy link
Copy Markdown
Member

Summary

  • Replaced intl_phone_field package with intl_country_data — we only used the country data, not the UI widget
  • Fixes phone number validation rejecting valid numbers for countries with incorrect min/max lengths in the old package (e.g. Finland required exactly 12 digits instead of the correct 7-14)
  • Updated all references in phone_setup_number_page.dart and phone_calls_page.dart to the new API

Test plan

  • Select Finland (+358) in the country picker and enter a valid mobile number (e.g. 40 1234567 — 9 digits) — should be accepted
  • Verify US numbers still work as before
  • Open the country picker, search by name/code/dial code — all should filter correctly
  • Complete full phone verification flow end-to-end

🤖 Generated with Claude Code

…lidation

The intl_phone_field package had incorrect min/max phone number lengths for
several countries (e.g. Finland required exactly 12 digits instead of 7-14),
causing valid phone numbers to be rejected. Switched to intl_country_data
which only provides country data without unused UI components.
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 26, 2026

Greptile Summary

This PR replaces the intl_phone_field dependency with intl_country_data, which has more accurate per-country telephone length constraints and fixes validation rejecting valid numbers (e.g. Finland). All usages are migrated from the old Country type to IntlCountryData, and all field name mappings (telephoneCode, telephoneMinLength, telephoneMaxLength, codeAlpha2, flag) are correct per the package's published API.\n\nKey changes:\n- pubspec.yaml / lock files: intl_phone_field: ^3.2.0intl_country_data: ^1.2.9\n- phone_setup_number_page.dart: CountryIntlCountryData throughout; _CountryPickerSheetState now caches the full list in _allCountries and filters over it (addressing prior feedback)\n- phone_calls_page.dart: _extractCountryCode now calls IntlCountryData.all() once before the loop rather than per iteration (addressing prior feedback)\n- Podfile.lock: Routine iOS pod bumps (SDWebImage, SwiftProtobuf, TwilioVoice)\n\nOne minor remaining cleanup: _CountryPickerSheetState._filtered is still initialized with a second IntlCountryData.all() call at field-declaration level — in Dart, field initializers cannot reference this, so reusing _allCountries would require moving the initialization into initState.

Confidence Score: 5/5

Safe to merge — the migration is API-correct, fixes a real validation bug, and prior review feedback has been substantially addressed.

All field names used against the new package match its documented API. The two previously flagged IntlCountryData.all() hot-path calls have been fixed. The only remaining issue is a single redundant all() call during widget construction, which is a minor style point with no runtime impact. No regressions introduced.

app/lib/pages/phone_calls/phone_setup_number_page.dart — minor redundant IntlCountryData.all() at _filtered field init.

Important Files Changed

Filename Overview
app/lib/pages/phone_calls/phone_setup_number_page.dart Main page refactored to use intl_country_data API; all field mappings (telephoneCode, telephoneMinLength/Max, codeAlpha2, flag) are correct. Minor: _filtered still redundantly calls IntlCountryData.all() at field init instead of reusing _allCountries.
app/lib/pages/phone_calls/phone_calls_page.dart Import swapped to intl_country_data; _extractCountryCode now caches IntlCountryData.all() before the loop (addressing prior feedback). API field telephoneCode is correct.
app/pubspec.yaml Clean swap of intl_phone_field: ^3.2.0 for intl_country_data: ^1.2.9.
app/pubspec.lock Lock file updated: intl_phone_field replaced with intl_country_data 1.2.9; several transitive dependencies (characters, matcher, meta, material_color_utilities) downgraded slightly — likely from dependency resolution.
app/ios/Podfile.lock Routine Pod version bumps: SDWebImage 5.21.5→5.21.7, SwiftProtobuf 1.33.3→1.36.1, TwilioVoice 6.13.5→6.13.6. No concerns.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User taps country selector] --> B[_showCountryPicker]
    B --> C[_CountryPickerSheet opens]
    C --> D["_allCountries = IntlCountryData.all() [once at init]"]
    D --> E["_filtered = IntlCountryData.all() [redundant init — see comment]"]
    E --> F[Render country list]
    F --> G{User types in search}
    G -->|keystroke| H["_filter(query) — uses _allCountries cache"]
    H --> I[setState _filtered = filtered subset]
    I --> F
    G -->|selects country| J[_selectedCountry updated]
    J --> K[User enters phone digits]
    K --> L{_isValid check}
    L -->|"digits >= telephoneMinLength && digits <= telephoneMaxLength"| M[Enable Continue button]
    L -->|invalid| N[Button disabled]
    M --> O["_fullNumber = +telephoneCode + digits"]
    O --> P[startVerification called]
Loading

Reviews (2): Last reviewed commit: "Cache country list in CountryPickerSheet..." | Re-trigger Greptile

Comment thread backend/routers/apps.py Outdated
Comment thread backend/routers/apps.py Outdated
Comment on lines 402 to 408
for (var len = 3; len >= 1; len--) {
if (digits.length <= len) continue;
var candidate = digits.substring(0, len);
if (countries.any((c) => c.fullCountryCode == candidate)) {
if (IntlCountryData.all().any((c) => c.telephoneCode == candidate)) {
return '+$candidate';
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 IntlCountryData.all() called on every loop iteration

IntlCountryData.all() is invoked up to 3 times inside the loop (once per iteration when len is 3, 2, and 1). If all() allocates a new list each call, this creates unnecessary allocations on every invocation of _extractCountryCode. Cache the result before the loop:

Suggested change
for (var len = 3; len >= 1; len--) {
if (digits.length <= len) continue;
var candidate = digits.substring(0, len);
if (countries.any((c) => c.fullCountryCode == candidate)) {
if (IntlCountryData.all().any((c) => c.telephoneCode == candidate)) {
return '+$candidate';
}
}
String? _extractCountryCode(String e164Number) {
if (!e164Number.startsWith('+')) return null;
var digits = e164Number.substring(1); // strip '+'
final allCountries = IntlCountryData.all();
// Try longest match first (country codes are 1-3 digits)
for (var len = 3; len >= 1; len--) {
if (digits.length <= len) continue;
var candidate = digits.substring(0, len);
if (allCountries.any((c) => c.telephoneCode == candidate)) {
return '+$candidate';
}
}
return null;
}

Comment on lines 244 to 248
_filtered = IntlCountryData.all().where((c) {
return c.name.toLowerCase().contains(q) ||
c.telephoneCode.contains(q) ||
c.codeAlpha2.toLowerCase().contains(q);
}).toList();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 IntlCountryData.all() called on every search keystroke

IntlCountryData.all() is re-invoked on every call to _filter, which fires on each keystroke. Consider caching the full country list as a field and filtering over the cached list to avoid repeated allocations:

Suggested change
_filtered = IntlCountryData.all().where((c) {
return c.name.toLowerCase().contains(q) ||
c.telephoneCode.contains(q) ||
c.codeAlpha2.toLowerCase().contains(q);
}).toList();
_filtered = _allCountries.where((c) {
return c.name.toLowerCase().contains(q) ||
c.telephoneCode.contains(q) ||
c.codeAlpha2.toLowerCase().contains(q);
}).toList();

And add final List<IntlCountryData> _allCountries = IntlCountryData.all(); as a field on _CountryPickerSheetState.

Comment thread app/ios/Runner.xcodeproj/xcshareddata/xcschemes/prod.xcscheme Outdated
@mdmohsin7
Copy link
Copy Markdown
Member Author

@greptile-apps re-review

@mdmohsin7 mdmohsin7 merged commit 2265955 into main Mar 26, 2026
3 checks passed
@mdmohsin7 mdmohsin7 deleted the fix/replace-intl-phone-field-with-intl-country-data branch March 26, 2026 14:44
Glucksberg pushed a commit to Glucksberg/omi-local that referenced this pull request Apr 28, 2026
…lidation (BasedHardware#6063)

## Summary
- Replaced `intl_phone_field` package with `intl_country_data` — we only
used the country data, not the UI widget
- Fixes phone number validation rejecting valid numbers for countries
with incorrect min/max lengths in the old package (e.g. Finland required
exactly 12 digits instead of the correct 7-14)
- Updated all references in `phone_setup_number_page.dart` and
`phone_calls_page.dart` to the new API

## Test plan
- [ ] Select Finland (+358) in the country picker and enter a valid
mobile number (e.g. `40 1234567` — 9 digits) — should be accepted
- [ ] Verify US numbers still work as before
- [ ] Open the country picker, search by name/code/dial code — all
should filter correctly
- [ ] Complete full phone verification flow end-to-end

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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