Flutter电话号码格式化插件flutter_libphonenumber_android的使用
Flutter电话号码格式化插件flutter_libphonenumber_android的使用
简介
flutter_libphonenumber_android
是 Android 平台实现的一部分,用于 flutter_libphonenumber
插件。该插件允许你在 Flutter 应用中格式化和解析电话号码。
使用示例
以下是一个完整的示例,展示了如何使用 flutter_libphonenumber_android
插件来格式化和解析电话号码。
// ignore_for_file: avoid_print
import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_libphonenumber_platform_interface/flutter_libphonenumber_platform_interface.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
[@override](/user/override)
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late final _future = FlutterLibphonenumberPlatform.instance.init();
final phoneController = TextEditingController();
final countryController = TextEditingController(text: 'United States');
final manualFormatController = TextEditingController();
[@override](/user/override)
void initState() {
super.initState();
updatePlaceholderHint();
}
/// 结果为调用解析方法时的数据。
String? parsedData;
/// 用于格式化号码为移动或固定线路
var _globalPhoneType = PhoneNumberType.mobile;
/// 选择国际或国内电话格式
var _globalPhoneFormat = PhoneNumberFormat.international;
/// 当前选中的国家
var _currentSelectedCountry = const CountryWithPhoneCode.us();
var _placeholderHint = '';
var _inputContainsCountryCode = true;
/// 保持光标在输入的末尾
var _shouldKeepCursorAtEndOfInput = true;
void updatePlaceholderHint() {
late String newPlaceholder;
if (_globalPhoneType == PhoneNumberType.mobile) {
if (_globalPhoneFormat == PhoneNumberFormat.international) {
newPlaceholder =
_currentSelectedCountry.exampleNumberMobileInternational;
} else {
newPlaceholder = _currentSelectedCountry.exampleNumberMobileNational;
}
} else {
if (_globalPhoneFormat == PhoneNumberFormat.international) {
newPlaceholder =
_currentSelectedCountry.exampleNumberFixedLineInternational;
} else {
newPlaceholder = _currentSelectedCountry.exampleNumberFixedLineNational;
}
}
/// 去除提示中的国家代码
if (!_inputContainsCountryCode) {
newPlaceholder = newPlaceholder
.substring(_currentSelectedCountry.phoneCode.length + 2);
}
setState(() => _placeholderHint = newPlaceholder);
}
[@override](/user/override)
Widget build(final BuildContext context) {
return MaterialApp(
home: FutureBuilder<void>(
future: _future,
builder: (final context, final snapshot) {
if (snapshot.hasError) {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: const Text('flutter_libphonenumber'),
),
body: Center(
child: Text('错误: ${snapshot.error}'),
),
);
} else if (snapshot.connectionState == ConnectionState.done) {
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: const Text('flutter_libphonenumber'),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: SingleChildScrollView(
padding: EdgeInsets.only(
bottom: max(
0,
24 - MediaQuery.of(context).padding.bottom,
),
),
child: Column(
children: [
const SizedBox(height: 10),
/// 获取所有区域代码
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Column(
children: [
/// 打印区域数据
ElevatedButton(
child: const Text('打印所有区域数据'),
onPressed: () async {
// await FlutterLibphonenumber().init();
final res =
await FlutterLibphonenumberPlatform
.instance
.getAllSupportedRegions();
print(res['IT']);
print(res['US']);
print(res['BR']);
},
),
/// 间隔
const SizedBox(height: 12),
/// 国家代码输入
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 24,
),
child: TextField(
controller: countryController,
keyboardType: TextInputType.phone,
onChanged: (final v) {
setState(() {});
},
textAlign: TextAlign.center,
onTap: () async {
final sortedCountries = CountryManager()
.countries
..sort(
(final a, final b) =>
(a.countryName ?? '').compareTo(
b.countryName ?? '',
),
);
final res = await showModalBottomSheet<CountryWithPhoneCode>(
context: context,
isScrollControlled: false,
builder: (final context) {
return ListView.builder(
padding:
const EdgeInsets.symmetric(
vertical: 16,
),
itemBuilder:
(final context, final index) {
final item =
sortedCountries[index];
return GestureDetector(
behavior:
HitTestBehavior.opaque,
onTap: () {
Navigator.of(context)
.pop(item);
},
child: Padding(
padding: const EdgeInsets
.symmetric(
horizontal: 24,
vertical: 16,
),
child: Row(
children: [
/// 电话代码
Expanded(
child: Text(
'+${item.phoneCode}',
textAlign:
TextAlign.right,
),
),
/// 间隔
const SizedBox(
width: 16,
),
/// 名称
Expanded(
flex: 8,
child: Text(
item.countryName ??
'',
),
),
],
),
),
);
},
itemCount: sortedCountries.length,
);
},
);
print('新国家选择: $res');
if (res != null) {
setState(() {
_currentSelectedCountry = res;
});
updatePlaceholderHint();
countryController.text =
res.countryName ??
'+ ${res.phoneCode}';
}
},
readOnly: true,
inputFormatters: const [],
),
),
],
),
),
/// 间隔
const SizedBox(width: 20),
Expanded(
child: Column(
children: [
/// 移动或固定线路切换
Row(
children: [
Switch(
value: _globalPhoneType ==
PhoneNumberType.mobile
? true
: false,
onChanged: (final val) {
setState(
() =>
_globalPhoneType =
val == false
? PhoneNumberType.fixedLine
: PhoneNumberType.mobile,
);
updatePlaceholderHint();
},
),
/// 间隔
const SizedBox(width: 5),
Flexible(
child: _globalPhoneType ==
PhoneNumberType.mobile
? const Text('格式为移动')
: const Text('格式为固定线路'),
),
],
),
/// 国际或国内切换
Row(
children: [
Switch(
value: _globalPhoneFormat ==
PhoneNumberFormat.national
? true
: false,
onChanged: (final val) {
setState(
() =>
_globalPhoneFormat = val ==
false
? PhoneNumberFormat
.international
: PhoneNumberFormat.national,
);
updatePlaceholderHint();
},
),
/// 间隔
const SizedBox(width: 5),
Flexible(
child: _globalPhoneFormat ==
PhoneNumberFormat.national
? const Text('国内')
: const Text('国际'),
),
],
),
/// 格式化时假设国家代码存在或不存在
Row(
children: [
Switch(
value: _inputContainsCountryCode,
onChanged: (final val) {
setState(
() =>
_inputContainsCountryCode =
!_inputContainsCountryCode,
);
updatePlaceholderHint();
},
),
/// 间隔
const SizedBox(width: 5),
Flexible(
child: _inputContainsCountryCode
? const Text('包含国家代码')
: const Text('不包含国家代码'),
),
],
),
/// 切换光标保持在输入的末尾,允许用户编辑输入的中间部分。
Row(
children: [
Switch(
value: _shouldKeepCursorAtEndOfInput,
onChanged: (final val) {
setState(
() =>
_shouldKeepCursorAtEndOfInput =
!_shouldKeepCursorAtEndOfInput,
);
updatePlaceholderHint();
},
),
/// 间隔
const SizedBox(width: 5),
const Flexible(
child: Text('强制光标在末尾'),
),
],
),
],
),
),
],
),
/// 间隔
const SizedBox(height: 10),
const Divider(),
const SizedBox(height: 10),
/// 实时格式化
const Text(
'实时格式化(同步使用掩码)',
),
/// 电话输入
SizedBox(
width: 160,
child: TextField(
textAlign: TextAlign.center,
keyboardType: TextInputType.phone,
controller: phoneController,
decoration: InputDecoration(
hintText: _placeholderHint,
),
inputFormatters: [
LibPhonenumberTextFormatter(
phoneNumberType: _globalPhoneType,
phoneNumberFormat: _globalPhoneFormat,
country: _currentSelectedCountry,
inputContainsCountryCode:
_inputContainsCountryCode,
shouldKeepCursorAtEndOfInput:
_shouldKeepCursorAtEndOfInput,
),
],
),
),
/// 间隔
const SizedBox(height: 10),
const Text(
'如果国家代码不为空,电话号码将格式化为假定没有国家代码。',
style: TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
/// 间隔
const SizedBox(height: 20),
const Divider(),
const SizedBox(height: 20),
const Text(
'手动格式化/解析电话号码。\n异步使用 FlutterLibphonenumber().format()。\n同步使用 FlutterLibphonenumber().formatPhone。',
style: TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
/// 手动电话输入
SizedBox(
width: 180,
child: TextField(
keyboardType: TextInputType.phone,
textAlign: TextAlign.center,
controller: manualFormatController,
decoration: InputDecoration(
hintText: _placeholderHint,
),
),
),
/// 间隔
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
/// 手动格式化电话输入
Flexible(
child: ElevatedButton(
child: const Text(
'格式化(异步)',
textAlign: TextAlign.center,
),
onPressed: () async {
// 异步格式化,通过本地调用 libphonenumber
final res =
await FlutterLibphonenumberPlatform
.instance
.format(
manualFormatController.text,
_currentSelectedCountry.countryCode,
);
setState(
() =>
manualFormatController.text =
res['formatted'] ?? '',
);
},
),
),
/// 间隔
const SizedBox(width: 10),
Flexible(
child: ElevatedButton(
child: const Text(
'格式化(同步)',
textAlign: TextAlign.center,
),
onPressed: () async {
if (CountryManager().countries.isEmpty) {
print(
"警告:国家列表为空,这意味着 init 尚未运行。无法同步格式化,直到 init 已经执行。",
);
}
// 同步格式化,不通过本地调用 libphonenumber,而是直接通过 Dart 调用掩码输入
manualFormatController.text =
FlutterLibphonenumberPlatform.instance
.formatNumberSync(
manualFormatController.text,
country: _currentSelectedCountry,
phoneNumberType: _globalPhoneType,
phoneNumberFormat: _globalPhoneFormat,
inputContainsCountryCode:
_inputContainsCountryCode,
);
},
),
),
/// 间隔
const SizedBox(width: 10),
/// 手动格式化电话输入
Flexible(
child: ElevatedButton(
child: const Text(
'解析',
textAlign: TextAlign.center,
),
onPressed: () async {
try {
final res =
await FlutterLibphonenumberPlatform
.instance
.parse(
manualFormatController.text,
region:
_currentSelectedCountry.countryCode,
);
const JsonEncoder encoder =
JsonEncoder.withIndent(' ');
setState(
() =>
parsedData = encoder.convert(res),
);
} catch (e) {
print(e);
setState(() => parsedData = null);
}
},
),
),
],
),
/// 间隔
const SizedBox(height: 10),
Text(parsedData ?? '无效的号码'),
],
),
),
),
),
);
} else {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: const Text('flutter_libphonenumber'),
),
body: const Center(
child: CircularProgressIndicator(),
),
);
}
},
),
);
}
}
更多关于Flutter电话号码格式化插件flutter_libphonenumber_android的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter电话号码格式化插件flutter_libphonenumber_android的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用flutter_libphonenumber_android
插件进行电话号码格式化的示例代码。这个插件允许你根据国际电话号码格式标准(E.164)来解析、格式化和验证电话号码。
1. 添加依赖
首先,你需要在你的pubspec.yaml
文件中添加flutter_libphonenumber_android
依赖:
dependencies:
flutter:
sdk: flutter
flutter_libphonenumber_android: ^0.2.5 # 请检查最新版本号
2. 导入插件
在你的Dart文件中导入插件:
import 'package:flutter_libphonenumber_android/flutter_libphonenumber_android.dart';
3. 初始化插件
在你的主应用文件(通常是main.dart
)中初始化插件:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: PhoneNumberFormatterScreen(),
);
}
}
4. 使用插件进行电话号码格式化
创建一个新的屏幕或页面,用于演示电话号码格式化功能:
import 'package:flutter/material.dart';
import 'package:flutter_libphonenumber_android/flutter_libphonenumber_android.dart';
class PhoneNumberFormatterScreen extends StatefulWidget {
@override
_PhoneNumberFormatterScreenState createState() => _PhoneNumberFormatterScreenState();
}
class _PhoneNumberFormatterScreenState extends State<PhoneNumberFormatterScreen> {
final TextEditingController _controller = TextEditingController();
String _formattedNumber = "";
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _formatPhoneNumber() async {
try {
var phoneNumberUtil = FlutterLibPhonenumberAndroid();
var numberProto = await phoneNumberUtil.parseNumber(_controller.text, "US"); // 假设默认国家代码为US
var formattedNumber = phoneNumberUtil.formatNumber(numberProto, PhoneNumberFormat.E164);
setState(() {
_formattedNumber = formattedNumber;
});
} catch (e) {
print("Error formatting number: $e");
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Phone Number Formatter'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'Enter Phone Number',
),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: _formatPhoneNumber,
child: Text('Format Number'),
),
SizedBox(height: 16),
Text(
'Formatted Number: $_formattedNumber',
style: TextStyle(fontSize: 18),
),
],
),
),
);
}
}
5. 运行应用
确保你已经连接了设备或启动了模拟器,然后运行以下命令来启动你的Flutter应用:
flutter run
注意事项
flutter_libphonenumber_android
插件依赖于Google的libphonenumber
库,它提供了强大的电话号码解析和格式化功能。- 在
_formatPhoneNumber
方法中,你需要指定一个默认的国家代码(例如"US"),这个代码用于解析未包含国家代码的电话号码。 - 电话号码格式化的结果将以E.164格式显示,这是国际标准的电话号码格式。
以上代码提供了一个基本的电话号码格式化功能示例,你可以根据需要进行扩展和修改。