Flutter三方库适配OpenHarmony【flutter_libphonenumber】——formatNumberSync() 同步格式化与 mask 匹配原理
本文介绍了Flutter三方库flutter_libphonenumber中formatNumberSync()方法的实现原理。该方法用于电话号码的同步格式化,全程在Dart侧执行,无需跨平台通信。文章详细分析了该方法的5个执行步骤:确定国家、获取Mask、创建PhoneMask、应用Mask以及可选去掉区号。重点讲解了PhoneMask.apply()核心算法,包括清理输入、处理区号不匹配情况以
前言
欢迎来到 Flutter三方库适配OpenHarmony 系列文章!本系列围绕 flutter_libphonenumber 这个 电话号码处理库 的鸿蒙平台适配,进行全面深入的技术分享。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net


上一篇我们追踪了 format() 异步格式化的完整调用链路。本篇将分析它的"孪生兄弟"——formatNumberSync()。与 format() 需要跨平台通信不同,formatNumberSync() 完全在 Dart 侧执行,无需 MethodChannel,通过预加载的 Mask 数据实现高性能的同步格式化。
formatNumberSync()是LibPhonenumberTextFormatter实时输入格式化的核心。理解它的 Mask 匹配算法,就理解了用户每次按键时号码是如何被实时格式化的。
一、formatNumberSync() 的定位
1.1 为什么需要同步格式化
| 场景 | 要求 | 适用方法 |
|---|---|---|
| 用户按键实时格式化 | 同步、< 1ms | formatNumberSync() |
| 手动点击格式化按钮 | 异步可接受 | format() |
| 批量格式化号码列表 | 同步更高效 | formatNumberSync() |
TextInputFormatter.formatEditUpdate() 是同步方法,不能使用 async/await。因此实时输入格式化 必须 使用同步方案。
1.2 执行位置对比
format()(异步):
Dart → MethodChannel → ArkTS → 格式化 → 回传 → Dart
formatNumberSync()(同步):
Dart → Mask匹配 → 完成(全程在 Dart 侧)
二、完整源码分析
2.1 方法签名
String formatNumberSync(
final String number, {
final CountryWithPhoneCode? country,
final PhoneNumberType phoneNumberType = PhoneNumberType.mobile,
final PhoneNumberFormat phoneNumberFormat =
PhoneNumberFormat.international,
final bool removeCountryCodeFromResult = false,
final bool inputContainsCountryCode = true,
})
2.2 参数说明
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
number |
String | 必填 | 待格式化的号码 |
country |
CountryWithPhoneCode? | null | 指定国家(null 则自动检测) |
phoneNumberType |
PhoneNumberType | mobile | 手机/固话 |
phoneNumberFormat |
PhoneNumberFormat | international | 国际/国内格式 |
removeCountryCodeFromResult |
bool | false | 是否从结果中去掉区号 |
inputContainsCountryCode |
bool | true | 输入是否包含区号 |
2.3 完整实现
String formatNumberSync(
final String number, {
final CountryWithPhoneCode? country,
final PhoneNumberType phoneNumberType =
PhoneNumberType.mobile,
final PhoneNumberFormat phoneNumberFormat =
PhoneNumberFormat.international,
final bool removeCountryCodeFromResult = false,
final bool inputContainsCountryCode = true,
}) {
// ① 确定国家
final guessedCountry = country ??
CountryWithPhoneCode.getCountryDataByPhone(number);
if (guessedCountry == null) {
return number; // 无法识别国家,返回原始输入
}
// ② 获取 Mask 并应用
var formatResult = PhoneMask(
mask: guessedCountry.getPhoneMask(
format: phoneNumberFormat,
type: phoneNumberType,
removeCountryCodeFromMask: !inputContainsCountryCode,
),
country: guessedCountry,
).apply(number);
// ③ 可选:去掉区号
if (removeCountryCodeFromResult
&& inputContainsCountryCode) {
formatResult = formatResult.substring(
guessedCountry.phoneCode.length + 2);
}
return formatResult;
}
三、执行流程详解(5 步)
3.1 步骤 ① — 确定国家
final guessedCountry = country ??
CountryWithPhoneCode.getCountryDataByPhone(number);
两种方式确定国家:
| 方式 | 条件 | 说明 |
|---|---|---|
| 直接指定 | country != null |
调用者明确传入国家 |
| 自动检测 | country == null |
通过号码前缀匹配 |
自动检测使用 getCountryDataByPhone(),它通过递归缩短匹配算法从号码中识别国家区号(详见第 5 篇文章)。
3.2 步骤 ② — 获取 Mask
guessedCountry.getPhoneMask(
format: phoneNumberFormat,
type: phoneNumberType,
removeCountryCodeFromMask: !inputContainsCountryCode,
)
getPhoneMask() 根据 format × type 的组合返回对应的 Mask:
| format | type | CN 的 Mask |
|---|---|---|
| international | mobile | +00 000 0000 0000 |
| national | mobile | 000 0000 0000 |
| international | fixedLine | +00 00 0000 0000 |
| national | fixedLine | 000 0000 0000 |
当 inputContainsCountryCode = false 时,removeCountryCodeFromMask = true,Mask 会去掉区号部分:
原始 Mask: +00 000 0000 0000
去掉区号: 000 0000 0000
3.3 步骤 ③ — 创建 PhoneMask
PhoneMask(
mask: '+00 000 0000 0000', // 从步骤②获取
country: guessedCountry, // CN
)
PhoneMask 是一个简单的数据类,持有 Mask 字符串和国家信息。
3.4 步骤 ④ — 应用 Mask
phoneMask.apply(number)
这是核心步骤,下一节详细分析。
3.5 步骤 ⑤ — 可选去掉区号
if (removeCountryCodeFromResult
&& inputContainsCountryCode) {
formatResult = formatResult.substring(
guessedCountry.phoneCode.length + 2);
}
当同时满足两个条件时,从结果中截掉区号部分:
格式化结果: +86 131 2345 6789
phoneCode: 86(长度 2)
截取位置: 2 + 2 = 4(+号1 + 区号2 + 空格1)
最终结果: 131 2345 6789
四、PhoneMask.apply() 核心算法
4.1 完整源码
String apply(final String inputString) {
if (mask.isEmpty) return inputString;
// ① 清理输入:去掉所有非数字字符
var cleanedInput =
inputString.replaceAll(RegExp(r'\D'), '');
// ② 处理区号不匹配的情况
if (!mask.startsWith('+')
&& inputString.startsWith('+')) {
cleanedInput = cleanedInput.replaceFirst(
RegExp('^${country.phoneCode}'), '');
}
final chars = cleanedInput.split('');
final result = <String>[];
var index = 0;
// ③ 逐位匹配 Mask
for (var i = 0; i < mask.length; i++) {
if (index >= chars.length) break;
final curChar = chars[index];
if (mask[i] == '0') {
// Mask 位是 '0':用输入的数字替换
if (_isDigit(curChar)) {
result.add(curChar);
index++;
} else {
break;
}
} else {
// Mask 位是非数字:直接添加格式字符
result.add(mask[i]);
}
}
return result.join();
}
4.2 算法步骤
输入: '+8613123456789'
Mask: '+00 000 0000 0000'
步骤①: 清理输入
'+8613123456789' → '8613123456789'(去掉+)
步骤②: 区号检查
Mask 以 '+' 开头 → 不需要去掉区号
步骤③: 逐位匹配
chars = ['8','6','1','3','1','2','3','4','5','6','7','8','9']
index = 0
i=0: mask[0]='+' → 非数字 → result=['+']
i=1: mask[1]='0' → chars[0]='8' → result=['+','8'], index=1
i=2: mask[2]='0' → chars[1]='6' → result=['+','8','6'], index=2
i=3: mask[3]=' ' → 非数字 → result=['+','8','6',' ']
i=4: mask[4]='0' → chars[2]='1' → result=[..,'1'], index=3
i=5: mask[5]='0' → chars[3]='3' → result=[..,'3'], index=4
i=6: mask[6]='0' → chars[4]='1' → result=[..,'1'], index=5
i=7: mask[7]=' ' → 非数字 → result=[..,' ']
...依次匹配...
结果: '+86 131 2345 6789'
4.3 区号不匹配的处理
当 Mask 不含区号但输入含区号时:
if (!mask.startsWith('+')
&& inputString.startsWith('+')) {
cleanedInput = cleanedInput.replaceFirst(
RegExp('^${country.phoneCode}'), '');
}
示例:
Mask: '000 0000 0000'(National,不含+)
输入: '+8613123456789'
清理: '8613123456789'
去区号: '13123456789'(去掉开头的 '86')
匹配: '131 2345 6789'
4.4 Mask 字符的两种角色
| Mask 字符 | 角色 | 处理方式 |
|---|---|---|
0 |
数字占位符 | 用输入的下一个数字替换 |
其他(+, , -, (, )) |
格式字符 | 直接添加到输出 |
五、inputContainsCountryCode 的影响
5.1 两种输入模式
| 模式 | inputContainsCountryCode | 输入示例 | 说明 |
|---|---|---|---|
| 含区号 | true | +8613123456789 |
用户输入完整国际号码 |
| 不含区号 | false | 13123456789 |
用户只输入国内号码 |
5.2 对 Mask 选择的影响
guessedCountry.getPhoneMask(
format: phoneNumberFormat,
type: phoneNumberType,
removeCountryCodeFromMask: !inputContainsCountryCode,
// ↑ 取反!
)
| inputContainsCountryCode | removeCountryCodeFromMask | Mask(CN, mobile, intl) |
|---|---|---|
| true | false | +00 000 0000 0000 |
| false | true | 000 0000 0000 |
5.3 完整示例
含区号模式:
输入: '+8613123456789'
Mask: '+00 000 0000 0000'
结果: '+86 131 2345 6789'
不含区号模式:
输入: '13123456789'
Mask: '000 0000 0000'
结果: '131 2345 6789'
六、多国同步格式化结果对比
6.1 国际格式(含区号)
以下是 10 个主要国家使用 formatNumberSync() 的实际格式化结果:
| 国家 | 输入 | Mask | 输出 |
|---|---|---|---|
| CN | +8613123456789 |
+00 000 0000 0000 |
+86 131 2345 6789 |
| US | +12015550123 |
+0 000-000-0000 |
+1 201-555-0123 |
| GB | +447400123456 |
+00 0000 000000 |
+44 7400 123456 |
| JP | +819012345678 |
+00 000 0000 0000 |
+81 901 2345 6789 |
| DE | +4915123456789 |
+00 0000 0000 0000 |
+49 1512 3456 789 |
| FR | +33612345678 |
+00 0 00 00 00 00 |
+33 6 12 34 56 78 |
| AU | +61412345678 |
+00 000 000 000 |
+61 412 345 678 |
| BR | +5511912345678 |
+00 (00) 00000-0000 |
+55 (11) 91234-5678 |
| IN | +918123456789 |
+00 00000 00000 |
+91 81234 56789 |
| RU | +79123456789 |
+0 000 000-00-00 |
+7 912 345-67-89 |
6.2 国内格式(不含区号)
当 inputContainsCountryCode = false 时,使用去掉区号的 Mask:
| 国家 | 输入 | Mask(去区号后) | 输出 |
|---|---|---|---|
| CN | 13123456789 |
000 0000 0000 |
131 2345 6789 |
| US | 2015550123 |
(000) 000-0000 |
(201) 555-0123 |
| GB | 7400123456 |
0000 000000 |
7400 123456 |
| JP | 9012345678 |
000 0000 0000 |
901 2345 6789 |
| FR | 612345678 |
0 00 00 00 00 |
6 12 34 56 78 |
6.3 手机号 vs 固话号
同一个国家,手机号和固话号使用不同的 Mask:
| 国家 | 类型 | 输入 | Mask | 输出 |
|---|---|---|---|---|
| CN | mobile | +8613123456789 |
+00 000 0000 0000 |
+86 131 2345 6789 |
| CN | fixedLine | +861012345678 |
+00 00 0000 0000 |
+86 10 1234 5678 |
| GB | mobile | +447400123456 |
+00 0000 000000 |
+44 7400 123456 |
| GB | fixedLine | +441212345678 |
+00 000 000 0000 |
+44 121 234 5678 |
| JP | mobile | +819012345678 |
+00 000 0000 0000 |
+81 901 2345 6789 |
| JP | fixedLine | +81312345678 |
+00 0 0000 0000 |
+81 3 1234 5678 |
关键观察:手机号和固话号的 Mask 差异主要体现在 分组方式 上。例如中国手机号是 3-4-4 分组,固话是 2-4-4 分组(区号2位 + 号码8位)。
七、getCountryDataByPhone() 自动国家检测
7.1 当 country 参数为 null 时
如果调用 formatNumberSync() 时没有指定 country,会通过 getCountryDataByPhone() 自动检测:
final guessedCountry = country ??
CountryWithPhoneCode.getCountryDataByPhone(number);
7.2 自动检测的匹配过程
getCountryDataByPhone() 使用递归缩短匹配算法:
输入: '+8613123456789'
第 1 次: phoneCode = '+8613123456789'
→ 提取数字 '8613123456789' → 无匹配
第 2 次: phoneCode = '+861312345678'
→ 提取数字 '861312345678' → 无匹配
...
第 12 次: phoneCode = '+86'
→ 提取数字 '86' → 匹配 CN ✅
7.3 自动检测的局限性
| 局限 | 说明 | 示例 |
|---|---|---|
| 共享区号 | US 和 CA 都是 +1 | +12015550123 → 匹配到先注册的国家 |
| 无区号输入 | 不以 + 开头的号码 | 13123456789 → 无法匹配 |
| 短号码 | 输入太短无法匹配 | +8 → 无匹配 |
最佳实践:在已知用户所在国家的情况下,始终传入
country参数,避免自动检测的不确定性。
八、removeCountryCodeFromResult 参数
8.1 参数作用
removeCountryCodeFromResult 用于从格式化结果中 去掉区号部分:
if (removeCountryCodeFromResult
&& inputContainsCountryCode) {
formatResult = formatResult.substring(
guessedCountry.phoneCode.length + 2);
}
8.2 使用场景
| 场景 | removeCountryCodeFromResult | 输入 | 输出 |
|---|---|---|---|
| 显示完整国际号码 | false | +8613123456789 |
+86 131 2345 6789 |
| 只显示国内号码 | true | +8613123456789 |
131 2345 6789 |
8.3 与 inputContainsCountryCode 的配合
这两个参数必须配合使用:
| inputContainsCC | removeCC | 行为 |
|---|---|---|
| true | false | 输入含区号,输出含区号 |
| true | true | 输入含区号,输出去掉区号 |
| false | false | 输入不含区号,输出不含区号 |
| false | true | 无效组合(条件不满足,不执行截取) |
// 只有同时满足两个条件才截取
if (removeCountryCodeFromResult
&& inputContainsCountryCode) {
// 执行截取
}
注意:当
inputContainsCountryCode = false时,即使removeCountryCodeFromResult = true,也不会执行截取。因为输入本身就不含区号,结果中也不会有区号。
九、getPhoneMask() 方法详解
9.1 方法签名
String getPhoneMask({
required final PhoneNumberFormat format,
required final PhoneNumberType type,
final bool removeCountryCodeFromMask = false,
})
9.2 内部实现
String getPhoneMask({
required final PhoneNumberFormat format,
required final PhoneNumberType type,
final bool removeCountryCodeFromMask = false,
}) {
late String returnMask;
if (format == PhoneNumberFormat.international) {
if (type == PhoneNumberType.mobile) {
returnMask = phoneMaskMobileInternational;
} else {
returnMask = phoneMaskFixedLineInternational;
}
} else {
if (type == PhoneNumberType.mobile) {
returnMask = phoneMaskMobileNational;
} else {
returnMask = phoneMaskFixedLineNational;
}
}
if (removeCountryCodeFromMask
&& returnMask.startsWith('+')) {
returnMask =
returnMask.substring(phoneCode.length + 2);
}
return returnMask;
}
9.3 四种组合的 Mask 选择
getPhoneMask() 根据 format × type 的组合,从 4 个 Mask 字段中选择一个:
mobile fixedLine
┌──────────────────┐ ┌──────────────────┐
international │ phoneMaskMobile │ │ phoneMaskFixedLine│
│ International │ │ International │
└──────────────────┘ └──────────────────┘
national │ phoneMaskMobile │ │ phoneMaskFixedLine│
│ National │ │ National │
└──────────────────┘ └──────────────────┘
以中国为例:
| format | type | Mask |
|---|---|---|
| international | mobile | +00 000 0000 0000 |
| international | fixedLine | +00 00 0000 0000 |
| national | mobile | 000 0000 0000 |
| national | fixedLine | 000 0000 0000 |
以美国为例:
| format | type | Mask |
|---|---|---|
| international | mobile | +0 000 000 0000 |
| international | fixedLine | +0 000 000 0000 |
| national | mobile | (000) 000-0000 |
| national | fixedLine | (000) 000-0000 |
9.4 removeCountryCodeFromMask 的截取逻辑
当 removeCountryCodeFromMask = true 且 Mask 以 + 开头时:
returnMask = returnMask.substring(phoneCode.length + 2);
截取位置的计算:
Mask: +00 000 0000 0000
│││
│││
││└── 空格(1个字符)
│└─── 区号(phoneCode.length 个字符)
└──── +号(1个字符)
截取位置 = 1(+) + phoneCode.length + 1(空格)
= phoneCode.length + 2
不同国家的截取示例:
| 国家 | phoneCode | 长度 | 截取位置 | 原始 Mask | 截取后 |
|---|---|---|---|---|---|
| CN | 86 | 2 | 4 | +00 000 0000 0000 |
000 0000 0000 |
| US | 1 | 1 | 3 | +0 000 000 0000 |
000 000 0000 |
| GB | 44 | 2 | 4 | +00 0000 000000 |
0000 000000 |
| HK | 852 | 3 | 5 | +000 0000 0000 |
0000 0000 |
十、在 LibPhonenumberTextFormatter 中的完整使用
10.1 TextInputFormatter 的工作原理
Flutter 的 TextInputFormatter 是一个同步拦截器,在用户每次输入时被调用:
用户按键
│
↓ TextField 接收输入
│
↓ TextInputFormatter.formatEditUpdate(oldValue, newValue)
│ ├── 同步处理(不能 async)
│ └── 返回修改后的 TextEditingValue
│
↓ TextField 显示格式化后的文本
10.2 LibPhonenumberTextFormatter 的完整实现
class LibPhonenumberTextFormatter
extends TextInputFormatter {
final CountryWithPhoneCode country;
final PhoneNumberType phoneNumberType;
final PhoneNumberFormat phoneNumberFormat;
final bool inputContainsCountryCode;
final bool shouldKeepCursorAtEndOfInput;
LibPhonenumberTextFormatter({
required this.country,
this.phoneNumberType = PhoneNumberType.mobile,
this.phoneNumberFormat =
PhoneNumberFormat.international,
this.inputContainsCountryCode = true,
this.shouldKeepCursorAtEndOfInput = true,
});
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
final formatted =
FlutterLibphonenumberPlatform.instance
.formatNumberSync(
newValue.text,
country: country,
phoneNumberType: phoneNumberType,
phoneNumberFormat: phoneNumberFormat,
inputContainsCountryCode:
inputContainsCountryCode,
);
return TextEditingValue(
text: formatted,
selection: shouldKeepCursorAtEndOfInput
? TextSelection.collapsed(
offset: formatted.length)
: newValue.selection,
);
}
}
10.3 formatNumberSync 在 TextFormatter 中的调用频率
每次按键都会触发一次 formatEditUpdate(),进而调用一次 formatNumberSync():
用户输入 '+8613123456789'(14 个字符):
按键 1: '+' → formatNumberSync('+')
→ getCountryDataByPhone('+') → null
→ return '+'
按键 2: '8' → formatNumberSync('+8')
→ getCountryDataByPhone('+8') → null
→ return '+8'
按键 3: '6' → formatNumberSync('+86')
→ getCountryDataByPhone('+86') → CN ✅
→ mask = '+00 000 0000 0000'
→ PhoneMask.apply('+86') → '+86'
按键 4: '1' → formatNumberSync('+861')
→ CN, mask = '+00 000 0000 0000'
→ PhoneMask.apply('+861') → '+86 1'
...
按键 14: '9' → formatNumberSync('+8613123456789')
→ CN, mask = '+00 000 0000 0000'
→ PhoneMask.apply('+8613123456789')
→ '+86 131 2345 6789'
共调用 14 次 formatNumberSync()
每次耗时 < 0.1ms,总计 < 1.4ms
10.4 光标位置控制
shouldKeepCursorAtEndOfInput 参数控制格式化后光标的位置:
return TextEditingValue(
text: formatted,
selection: shouldKeepCursorAtEndOfInput
? TextSelection.collapsed(offset: formatted.length)
// 强制光标到末尾
: newValue.selection,
// 保持原始光标位置
);
| shouldKeepCursorAtEndOfInput | 光标行为 | 适用场景 |
|---|---|---|
| true(默认) | 每次格式化后光标跳到末尾 | 顺序输入 |
| false | 保持用户的光标位置 | 中间编辑 |
注意:当
shouldKeepCursorAtEndOfInput = false时,如果格式化改变了文本长度(如插入空格),光标位置可能不准确。这是一个已知的限制。
十一、与 format() 的深度对比
11.1 执行路径对比
format()(异步):
App → Dart format()
→ MethodChannel.invokeMapMethod()
→ StandardMessageCodec 编码
→ BinaryMessenger 传输
→ ArkTS onMethodCall()
→ handleFormat()
→ AsYouTypeFormatter.inputDigit() × N
→ Map → Record → result.success()
→ BinaryMessenger 回传
→ StandardMessageCodec 解码
→ Future<Map> 完成
总步骤: ~12 步
formatNumberSync()(同步):
App → Dart formatNumberSync()
→ getCountryDataByPhone()(可选)
→ getPhoneMask()
→ PhoneMask.apply()
→ return String
总步骤: ~4 步
11.2 格式化精度对比
| 对比项 | format() | formatNumberSync() |
|---|---|---|
| 格式化引擎 | AsYouTypeFormatter | PhoneMask |
| 国家专用格式 | 10 国有专用函数 | 统一 Mask 匹配 |
| 分隔符 | 按国家规则(空格/连字符/括号) | 由 Mask 决定 |
| 部分号码 | 支持(逐字符) | 支持(Mask 截断) |
11.3 结果差异示例
对于大多数国家,两者结果一致。但某些情况下可能有细微差异:
美国号码 +12015550123:
format(): '+1 201 555 0123'
formatNumberSync(): '+1 201-555-0123'(如果 Mask 含连字符)
差异来源:
format()使用AsYouTypeFormatter的通用formatPartialNumber()(用空格分隔),而formatNumberSync()使用预定义的 Mask(可能含连字符或括号)。
十二、在 LibPhonenumberTextFormatter 中的使用
12.1 调用位置
class LibPhonenumberTextFormatter
extends TextInputFormatter {
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
// 每次按键都调用 formatNumberSync
final formatted =
FlutterLibphonenumberPlatform.instance
.formatNumberSync(
newValue.text,
country: country,
phoneNumberType: phoneNumberType,
phoneNumberFormat: phoneNumberFormat,
inputContainsCountryCode:
inputContainsCountryCode,
);
// ...
}
}
12.2 调用频率
用户输入 '+8613123456789'(14 个字符):
按键 1: '+' → formatNumberSync('+') → '+'
按键 2: '8' → formatNumberSync('+8') → '+8'
按键 3: '6' → formatNumberSync('+86') → '+86'
按键 4: '1' → formatNumberSync('+861') → '+86 1'
...
按键 14: '9' → formatNumberSync('+8613123456789')
→ '+86 131 2345 6789'
共调用 14 次 formatNumberSync()
每次调用都必须在 微秒级 完成,否则用户会感受到输入延迟。这就是为什么必须使用同步方案。
十三、边界情况处理
13.1 空输入
formatNumberSync('')
→ getCountryDataByPhone('') → null
→ return '' // 返回原始空字符串
13.2 无法识别的国家
formatNumberSync('+999123456')
→ getCountryDataByPhone('+999123456') → null
→ return '+999123456' // 返回原始输入
13.3 输入比 Mask 短
Mask: '+00 000 0000 0000'
输入: '+861312'
清理: '861312'
匹配到 i=7 时 index >= chars.length → break
结果: '+86 131 2'
13.4 输入比 Mask 长
Mask: '+00 000 0000 0000'(14个数字位)
输入: '+86131234567890000'
清理: '86131234567890000'
匹配到 i=mask.length 时循环结束
多余的数字被忽略
结果: '+86 131 2345 6789'
总结
本文深入分析了 formatNumberSync() 同步格式化与 Mask 匹配原理。关键要点回顾:
formatNumberSync()完全在 Dart 侧执行,无需 MethodChannel 通信,耗时 < 0.1ms- 执行流程分 5 步:确定国家 → 获取 Mask → 创建 PhoneMask → 应用 Mask → 可选去区号
PhoneMask.apply()的核心算法是 逐位匹配:Mask 中的0用输入数字替换,其他字符直接输出inputContainsCountryCode参数决定是否使用含区号的 Mask,影响格式化结果- 与
format()相比,formatNumberSync()更快(~30 倍)但格式化精度依赖预定义 Mask - 它是
LibPhonenumberTextFormatter实时输入格式化的核心,每次按键都会调用
下一篇我们将分析 parse() 号码解析与元数据提取实现,了解如何从一个电话号码字符串中提取出 country_code、e164、national、international、type 等 7 个字段。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- OpenHarmony 适配仓库:gitcode.com/oh-flutter/flutter_libphonenumber
- 开源鸿蒙跨平台社区:openharmonycrossplatform.csdn.net
- Flutter TextInputFormatter:api.flutter.dev - TextInputFormatter
- Flutter Platform Channels:docs.flutter.dev - Platform channels
- Dart RegExp 文档:api.dart.dev - RegExp
- Google libphonenumber:github.com/google/libphonenumber
- ArkTS 语言文档:developer.huawei.com - ArkTS
- Flutter-OHOS 项目:gitee.com/openharmony-sig/flutter_flutter
- plugin_platform_interface:pub.dev/packages/plugin_platform_interface
更多推荐
所有评论(0)