VisorVisor
FlutterWidgets

VisorPhoneInput

International phone-number input with country picker, libphonenumber-backed formatting, and validation states matching VisorTextInput.

VisorPhoneInput extends VisorTextInput with a country picker in the prefix slot and a flutter_libphonenumber-backed validator. Selecting a country sets the formatter and per-country digit limit; entry auto-formats as the user types.

Preview

When to Use

  • Capturing an international phone number with country code
  • Forms where formatted entry (e.g. (555) 123-4567) aids comprehension
  • Sign-up / verification flows that need a parsed, validated phone number

When Not to Use

  • Generic numeric entry (use VisorTextInput with TextInputType.number)
  • Single-country apps that don't need a picker (use VisorTextInput)
  • When the consumer cannot accept the country_code_picker and flutter_libphonenumber dependencies

Installation

npx visor add phone-input --target flutter

Or copy components/flutter/visor_phone_input/visor_phone_input.dart into your project. Note: phone-input depends on text-input plus the pub packages country_code_picker ^3.0.0 and flutter_libphonenumber ^2.4.0.

Basic Usage

import 'package:ui/ui.dart';

VisorPhoneInput(
  labelText: 'Mobile number',
  controller: _phone,
)

With Country Override

VisorPhoneInput(
  labelText: 'Mobile number',
  controller: _phone,
  initialCountryCode: 'GB',
  onCountryChanged: (country) => _selectedCountry = country,
)

With Validation

VisorPhoneInput(
  labelText: 'Mobile number',
  controller: _phone,
  validator: (value) {
    if (value == null || value.isEmpty) return 'Required';
    return null;
  },
  autovalidateMode: AutovalidateMode.onUserInteraction,
)

API Reference

PropertyTypeDefaultDescription
labelTextStringrequiredFloating label text
controllerTextEditingController?nullOptional external text controller
focusNodeFocusNode?nullOptional external focus node
errorTextString?nullOverride the error message
onChangedValueChanged<String>?nullFires on every keystroke
onCountryChangedValueChanged<CountryCode>?nullFires when the user picks a different country
onFieldSubmittedValueChanged<String>?nullFires on keyboard submit
validatorString? Function(String?)?nullSynchronous validator
initialCountryCodeString'US'ISO country code to seed the picker
textInputActionTextInputAction?nullKeyboard action button type
autofocusboolfalseRequest focus on first build
enabledbooltrueWhen false, reduces opacity and ignores input
autovalidateModeAutovalidateMode?nullWhen the validator runs
semanticLabelString?nullOverride the announced label (defaults to labelText)

Accessibility

  • Country picker carries Semantics(button: true, label: 'Country code, [name] [dial code]. Tap to change.').
  • libphonenumber validation is treated as the canonical "is valid" signal — it overrides the validator when libphonenumber accepts.
  • All VisorTextInput a11y properties carry through.

Source

  • components/flutter/visor_phone_input/visor_phone_input.dart
  • Quality contract audit row: docs/flutter-widget-quality-contract.md (Rec8)