1
votes

I am trying to make a TextFormField that formats a phone number as you type it. I am trying to use the libphonenumber package to do this, but the method it provides returns a Future<String>, not a String. I can't figure out how to make it work.

My input field looks like this:

TextFormField(
    decoration: InputDecoration(
      hintText: "Phone Number",
    ),
    controller: _textController,
    keyboardType: TextInputType.phone,
    inputFormatters: <TextInputFormatter>[_PhoneFormatter('US')],
  )

and the formatter code looks like this:

import 'package:libphonenumber/libphonenumber.dart';

class _PhoneFormatter extends TextInputFormatter {
  final String _countryCode;

  _PhoneFormatter(this._countryCode);

  @override
  TextEditingValue formatEditUpdate(oldPhone, newPhone) {
    final phoneNumber = PhoneNumberUtil.formatAsYouType(
        phoneNumber: newPhone.text, isoCode: _countryCode);
    return TextEditingValue(
        text: phoneNumber,
        selection: TextSelection.collapsed(offset: phoneNumber.length));
  }
}

but I get the error:

The argument type 'Future<String' can't be assigned to the parameter type 'String'.

I tried to re-write it with "async/await":

  @override
  TextEditingValue formatEditUpdate(oldPhone, newPhone) async {
    final phoneNumber = await PhoneNumberUtil.formatAsYouType(
        phoneNumber: newPhone.text, isoCode: _countryCode);
    return TextEditingValue(
        text: phoneNumber,
        selection: TextSelection.collapsed(offset: phoneNumber.length));
  }

but I then get the error:

Functions marked 'async' must have a return type assignable to 'Future'.

How can I make this work?

2

2 Answers

1
votes

So I was able to find a solution to my problem. I found another package (intl_phone_number_input) that utilized the same feature of libphonenumber, and I saw how they did it (thanks to [email protected]). They require an async callback method as a parameter to the TextInputFormatter, and then call it during the edit processing. In another million years I would not have figured this out on my own...

Here is my updated formatter code:

import 'package:libphonenumber/libphonenumber.dart';

class _PhoneFormatter extends TextInputFormatter {
  final String _countryCode;
  final void Function(TextEditingValue value) onPhoneFormatted;

  _PhoneFormatter(this._countryCode, {@required this.onPhoneFormatted});

  @override
  TextEditingValue formatEditUpdate(oldPhone, newPhone) {
    _formatPhone(newPhone.text.replaceAll(RegExp(r'[^\d\+]+'), ''))
        .then((String phone) {
      onPhoneFormatted(TextEditingValue(
          text: phone,
          selection: TextSelection.collapsed(offset: phone.length)));
    });
    return newPhone;
  }

  Future<String> _formatPhone(String phoneNumber) async {
    try {
      String formattedPhoneNumber = await PhoneNumberUtil.formatAsYouType(
          phoneNumber: phoneNumber, isoCode: _countryCode);
      return formattedPhoneNumber ?? phoneNumber;
    } on Exception {
      return phoneNumber;
    }
  }
}

and my input field that utilizes it:

final _countryCode = 'US'

TextFormField(
  decoration: InputDecoration(
    hintText: "Phone Number",
  ),
  controller: _textController,
  keyboardType: TextInputType.phone,
  inputFormatters: <TextInputFormatter>[
    _PhoneFormatter(
      _countryCode,
      onPhoneFormatted: (TextEditingValue value) {
        setState(() {
          _textController.value = value;
        });
      },
    )
  ],
)

Seems to work great.

0
votes

You can use the mask_text_input_formatter package for the flexible phone formatting:

TextFormField(
  decoration: InputDecoration(
    hintText: "Phone Number",
  ),
  controller: _textController,
  keyboardType: TextInputType.phone,
  inputFormatters: [phoneFormatter()],
)

//...

MaskTextInputFormatter phoneFormatter(){
  return MaskTextInputFormatter(mask: '+#-###-###-####', filter: { "#": RegExp(r'[0-9]') });
}