Flutter电话号码格式化插件flutter_libphonenumber_ios的使用
Flutter电话号码格式化插件flutter_libphonenumber_ios的使用
flutter_libphonenumber_ios
是 flutter_libphonenumber
插件的 iOS 平台实现,用于在 Flutter 应用中处理电话号码的格式化和解析。本文将介绍如何使用该插件,并提供一个完整的示例 Demo。
示例代码
以下是一个完整的示例应用,展示了如何使用 flutter_libphonenumber_ios
插件来格式化和解析电话号码。
// 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();
}
/// Result when we call the parse method.
String? parsedData;
/// Used to format numbers as mobile or land line
var _globalPhoneType = PhoneNumberType.mobile;
/// Use international or national phone format
var _globalPhoneFormat = PhoneNumberFormat.international;
/// Current selected country
var _currentSelectedCountry = const CountryWithPhoneCode.us();
var _placeholderHint = '';
var _inputContainsCountryCode = true;
/// Keep cursor on the end
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;
}
}
/// Strip country code from hint
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('error: ${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),
/// Get all region codes
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Column(
children: [
/// Print region data
ElevatedButton(
child: const Text('Print all region data'),
onPressed: () async {
final res =
await FlutterLibphonenumberPlatform
.instance
.getAllSupportedRegions();
print(res['IT']);
print(res['US']);
print(res['BR']);
},
),
/// Spacer
const SizedBox(height: 12),
/// Country code input
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: [
/// Phone code
Expanded(
child: Text(
'+${item.phoneCode}',
textAlign:
TextAlign.right,
),
),
/// Spacer
const SizedBox(
width: 16,
),
/// Name
Expanded(
flex: 8,
child: Text(
item.countryName ??
'',
),
),
],
),
),
);
},
itemCount: sortedCountries.length,
);
},
);
print('New country selection: $res');
if (res != null) {
setState(() {
_currentSelectedCountry = res;
});
updatePlaceholderHint();
countryController.text =
res.countryName ??
'+ ${res.phoneCode}';
}
},
readOnly: true,
inputFormatters: const [],
),
),
],
),
),
/// Spacer
const SizedBox(width: 20),
Expanded(
child: Column(
children: [
/// Mobile or land line toggle
Row(
children: [
Switch(
value: _globalPhoneType ==
PhoneNumberType.mobile
? true
: false,
onChanged: (final val) {
setState(
() => _globalPhoneType =
val == false
? PhoneNumberType.fixedLine
: PhoneNumberType.mobile,
);
updatePlaceholderHint();
},
),
/// Spacer
const SizedBox(width: 5),
Flexible(
child: _globalPhoneType ==
PhoneNumberType.mobile
? const Text('Format as Mobile')
: const Text('Format as FixedLine'),
),
],
),
/// National or international line toggle
Row(
children: [
Switch(
value: _globalPhoneFormat ==
PhoneNumberFormat.national
? true
: false,
onChanged: (final val) {
setState(
() => _globalPhoneFormat = val ==
false
? PhoneNumberFormat
.international
: PhoneNumberFormat.national,
);
updatePlaceholderHint();
},
),
/// Spacer
const SizedBox(width: 5),
Flexible(
child: _globalPhoneFormat ==
PhoneNumberFormat.national
? const Text('National')
: const Text('International'),
),
],
),
/// Format assuming country code present or absent
Row(
children: [
Switch(
value: _inputContainsCountryCode,
onChanged: (final val) {
setState(
() => _inputContainsCountryCode =
!_inputContainsCountryCode,
);
updatePlaceholderHint();
},
),
/// Spacer
const SizedBox(width: 5),
Flexible(
child: _inputContainsCountryCode
? const Text('With country code')
: const Text('No country code'),
),
],
),
/// Toggle keeping the cursor in the same spot as it was when inputting, allowing
/// user to edit the middle of the input.
Row(
children: [
Switch(
value: _shouldKeepCursorAtEndOfInput,
onChanged: (final val) {
setState(
() => _shouldKeepCursorAtEndOfInput =
!_shouldKeepCursorAtEndOfInput,
);
updatePlaceholderHint();
},
),
/// Spacer
const SizedBox(width: 5),
const Flexible(
child: Text('Force cursor to end'),
),
],
),
],
),
),
],
),
/// Spacer
const SizedBox(height: 10),
const Divider(),
const SizedBox(height: 10),
/// Format as you type
const Text(
'Format as you type (synchronous using masks)',
),
/// Phone input
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,
),
],
),
),
/// Spacer
const SizedBox(height: 10),
const Text(
'If country code is not empty, phone number will format expecting no country code.',
style: TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
/// Spacer
const SizedBox(height: 20),
const Divider(),
const SizedBox(height: 20),
const Text(
'Manually format / parse the phone number.\nAsync uses FlutterLibphonenumber().format().\nSync uses FlutterLibphonenumber().formatPhone.',
style: TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
/// Manual Phone input
SizedBox(
width: 180,
child: TextField(
keyboardType: TextInputType.phone,
textAlign: TextAlign.center,
controller: manualFormatController,
decoration: InputDecoration(
hintText: _placeholderHint,
),
),
),
/// Spacer
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
/// Manually format the phone input
Flexible(
child: ElevatedButton(
child: const Text(
'Format (Async)',
textAlign: TextAlign.center,
),
onPressed: () async {
// Asynchronous formatting with native call into libphonenumber
final res =
await FlutterLibphonenumberPlatform
.instance
.format(
manualFormatController.text,
_currentSelectedCountry.countryCode,
);
setState(
() => manualFormatController.text =
res['formatted'] ?? '',
);
},
),
),
/// Spacer
const SizedBox(width: 10),
Flexible(
child: ElevatedButton(
child: const Text(
'Format (Sync)',
textAlign: TextAlign.center,
),
onPressed: () async {
if (CountryManager().countries.isEmpty) {
print(
"Warning: countries list is empty which means init hs not be run yet. Can't format synchronously until init has been executed.",
);
}
// Synchronous formatting with no native call into libphonenumber, just a dart call to mask the input
manualFormatController.text =
FlutterLibphonenumberPlatform.instance
.formatNumberSync(
manualFormatController.text,
country: _currentSelectedCountry,
phoneNumberType: _globalPhoneType,
phoneNumberFormat: _globalPhoneFormat,
inputContainsCountryCode:
_inputContainsCountryCode,
);
},
),
),
/// Spacer
const SizedBox(width: 10),
/// Manually format the phone input
Flexible(
child: ElevatedButton(
child: const Text(
'Parse',
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);
}
},
),
),
],
),
/// Spacer
const SizedBox(height: 10),
Text(parsedData ?? 'Number invalid'),
],
),
),
),
),
);
} else {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: const Text('flutter_libphonenumber'),
),
body: const Center(
child: CircularProgressIndicator(),
),
);
}
},
),
);
}
}
更多关于Flutter电话号码格式化插件flutter_libphonenumber_ios的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter电话号码格式化插件flutter_libphonenumber_ios的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中集成并使用flutter_libphonenumber_ios
插件来进行电话号码格式化的示例代码。需要注意的是,flutter_libphonenumber_ios
这个插件名称听起来是专门用于iOS的,但在实际使用中,你可能还会需要一个跨平台的插件如libphonenumber-js
或flutter_phone_direct_caller
,这些插件通常也支持iOS和Android。不过,为了遵循你的要求,这里假设我们使用的是flutter_libphonenumber_ios
(尽管这样的插件名较为罕见,且可能实际指向的是iOS特定的实现)。
首先,确保你的Flutter环境已经设置好,并且你已经有一个Flutter项目。
1. 添加依赖
在你的pubspec.yaml
文件中添加flutter_libphonenumber_ios
依赖(注意:如果实际插件名为其他,请替换为正确的插件名):
dependencies:
flutter:
sdk: flutter
flutter_libphonenumber_ios: ^x.y.z # 替换x.y.z为实际版本号
然后运行flutter pub get
来安装依赖。
2. 配置iOS项目
由于插件名暗示它可能只支持iOS,你可能需要在iOS原生代码中进行一些配置。不过,大多数Flutter插件现在都能自动处理这些配置。如果插件文档中有提到需要手动配置,请按照文档操作。
3. 使用插件
在你的Flutter代码中,你可以这样使用插件来格式化电话号码:
import 'package:flutter/material.dart';
import 'package:flutter_libphonenumber_ios/flutter_libphonenumber_ios.dart'; // 假设插件名正确
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Phone Number Formatter'),
),
body: PhoneNumberFormatterScreen(),
),
);
}
}
class PhoneNumberFormatterScreen extends StatefulWidget {
@override
_PhoneNumberFormatterScreenState createState() => _PhoneNumberFormatterScreenState();
}
class _PhoneNumberFormatterScreenState extends State<PhoneNumberFormatterScreen> {
final TextEditingController _controller = TextEditingController();
String _formattedNumber = '';
void _formatPhoneNumber() async {
try {
String rawNumber = _controller.text;
var formatted = await FlutterLibPhonenumberIos.parseAndFormatPhoneNumber(rawNumber);
setState(() {
_formattedNumber = formatted;
});
} catch (e) {
print("Error formatting phone number: $e");
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextField(
controller: _controller,
decoration: InputDecoration(
label Text: 'Enter Phone Number',
),
),
),
Sized BoxSized(Boxheight(:height :1 61),6
),
$_Elev atedTextButton('(Formatted
Number :onPressed: _formatPhoneNumber,
child: Text('Format Phone Number'),formattedNumber'),
],
),
);
}
}
注意事项
-
插件可用性:
flutter_libphonenumber_ios
这个插件名可能不是真实存在的,或者它可能是一个特定于iOS的插件。在实际项目中,你可能需要使用一个支持多平台的插件,如libphonenumber-dart
或flutter_phone_formatter
。 -
错误处理:在实际应用中,你需要添加更多的错误处理逻辑来处理各种可能的异常情况,比如无效的电话号码、网络问题等。
-
UI/UX:上述代码中的UI非常简单,你可能需要根据实际需求进行定制和优化。
-
插件文档:务必查阅插件的官方文档,了解如何正确集成和使用插件,以及如何处理任何可能的平台特定配置。
希望这能帮助你在Flutter项目中实现电话号码格式化功能!