Flutter电话号码格式化插件flutter_libphonenumber_web的使用

Flutter电话号码格式化插件flutter_libphonenumber_web的使用

flutter_libphonenumber_webflutter_libphonenumber 在 Web 平台上的实现。它允许你在 Web 应用中格式化和解析电话号码。

完整示例代码

以下是一个完整的示例,展示了如何在 Flutter 应用中使用 flutter_libphonenumber_web 插件来格式化和解析电话号码。

// 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('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),

                        /// 获取所有区域代码
                        Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            Expanded(
                              child: Column(
                                children: [
                                  /// 打印区域数据
                                  ElevatedButton(
                                    child: const Text('打印所有区域数据'),
                                    onPressed: () async {
                                      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_web的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter电话号码格式化插件flutter_libphonenumber_web的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何使用 flutter_libphonenumber_web 插件来格式化电话号码的示例代码。这个插件利用 Google 的 libphonenumber 库,可以在 Flutter 应用中对电话号码进行格式化和验证。

首先,确保在你的 pubspec.yaml 文件中添加 flutter_libphonenumber_web 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_libphonenumber_web: ^x.y.z  # 请替换为最新版本号

然后运行 flutter pub get 来获取依赖。

接下来,在你的 Flutter 项目中,你可以按照以下方式使用 flutter_libphonenumber_web 插件:

import 'package:flutter/material.dart';
import 'package:flutter_libphonenumber_web/flutter_libphonenumber_web.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Phone Number Formatter',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PhoneNumberFormatterScreen(),
    );
  }
}

class PhoneNumberFormatterScreen extends StatefulWidget {
  @override
  _PhoneNumberFormatterScreenState createState() => _PhoneNumberFormatterScreenState();
}

class _PhoneNumberFormatterScreenState extends State<PhoneNumberFormatterScreen> {
  final TextEditingController _controller = TextEditingController();
  String _formattedNumber = '';

  @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',
                border: OutlineInputBorder(),
              ),
              keyboardType: TextInputType.phone,
            ),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                _formatPhoneNumber();
              },
              child: Text('Format Phone Number'),
            ),
            SizedBox(height: 16),
            Text(
              'Formatted Number: $_formattedNumber',
              style: TextStyle(fontSize: 18),
            ),
          ],
        ),
      ),
    );
  }

  void _formatPhoneNumber() async {
    final phoneUtil = PhoneNumberUtil();
    try {
      final number = await phoneUtil.parseAndKeepRawInput(_controller.text, 'US'); // 假设默认国家代码为美国
      final formattedNumber = phoneUtil.format(number, PhoneNumberFormat.INTERNATIONAL);
      setState(() {
        _formattedNumber = formattedNumber;
      });
    } catch (e) {
      // 处理解析错误
      print('Error formatting phone number: $e');
    }
  }
}

在这个示例中,我们创建了一个简单的 Flutter 应用,用户可以输入一个电话号码,并点击按钮来格式化该号码。我们使用 flutter_libphonenumber_web 插件中的 PhoneNumberUtil 类来解析和格式化电话号码。

请注意,这个示例假设默认的国家代码是美国 ('US'),你可以根据实际需求更改这个值。另外,这个插件只支持在 Web 平台上使用,如果你在移动平台上需要类似的功能,可以考虑使用 libphonenumber 的其他 Flutter 插件,如 flutter_libphonenumber

确保在测试时,你的 Flutter 应用运行在支持 Web 的环境中。

回到顶部