Flutter三方库 dlibphonenumber 适配 OpenHarmony —— 获取运营商信息
在移动开发领域,我们总是面临着选择与适配。今天,你的Flutter应用在Android和iOS上跑得正欢,明天可能就需要考虑一个新的平台:HarmonyOS(鸿蒙)。这不是一道选答题,而是很多团队正在面对的现实。Flutter的优势很明确——写一套代码,就能在两个主要平台上运行,开发体验流畅。而鸿蒙代表的是下一个时代的互联生态,它不仅仅是手机系统,更着眼于未来全场景的体验。
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
目录
前言:跨生态开发中的实用工具
在移动应用开发中,用户输入验证是确保数据质量和用户体验的重要环节。特别是电话号码验证,它不仅关系到用户信息的准确性,还直接影响到后续的业务流程,如短信验证码发送、用户身份验证等。
随着Flutter跨平台技术的发展,开发者可以使用一套代码为多个平台构建应用。而HarmonyOS(鸿蒙)作为新兴的智能终端操作系统,为开发者提供了更广阔的应用场景。将电话号码验证功能适配到鸿蒙平台,是跨生态开发中的一个典型案例。
本项目实现了一个功能完整的电话号码验证与运营商信息查询工具,它不仅能够验证电话号码的格式是否正确,还能根据号码前缀识别运营商信息。通过这个实用工具,我们可以了解如何在Flutter for OpenHarmony项目中实现输入验证、异步操作和用户交互等核心功能。
混合工程结构深度解析
项目目录架构
当Flutter项目集成鸿蒙支持后,典型的项目结构会发生显著变化。以下是经过ohos_flutter插件初始化后的项目结构:
fluuter_openHarmony1/
├── lib/ # Flutter业务代码
│ ├── main.dart # 应用入口
│ ├── phone_number_validator_widget.dart # 电话号码验证组件
│ └── dlibphonenumber.dart # 电话号码验证工具类
├── ohos/ # 鸿蒙原生层
│ ├── AppScope/ # 应用作用域
│ │ └── resources/ # 应用资源
│ ├── entry/ # 主模块
│ │ └── src/main/
│ │ ├── ets/ # ArkTS代码
│ │ │ ├── entryability/ # 入口能力
│ │ │ └── pages/ # 页面
│ │ ├── resources/ # 鸿蒙资源文件
├── pubspec.yaml # Flutter依赖配置
└── 模版.md # 项目文档
展示效果图片
flutter 实时预览 效果展示
运行到鸿蒙虚拟设备中效果展示
功能代码实现
电话号码验证工具类实现
在本项目中,我们实现了一个模拟的 dlibphonenumber 工具类,用于电话号码的解析和验证。这个工具类虽然是模拟实现,但提供了与真实库类似的功能接口,便于后续扩展。
核心实现代码 引入dlibphonenumber
// 模拟 dlibphonenumber 库的实现
class PhoneNumber {
final String nationalNumber;
final String countryCode;
PhoneNumber({required this.nationalNumber, required this.countryCode});
}
class PhoneNumberUtil {
Future<PhoneNumber> parse(String phoneNumber, String countryCode) async {
// 简单的解析实现
return PhoneNumber(
nationalNumber: phoneNumber.replaceAll(RegExp(r'\D'), ''),
countryCode: countryCode,
);
}
Future<bool> isValidNumber(PhoneNumber phoneNumber) async {
// 简单的验证逻辑:检查是否是11位数字
return phoneNumber.nationalNumber.length == 11;
}
}
设计说明与使用方法
- PhoneNumber 类:用于存储解析后的电话号码信息,包含国家代码和电话号码两个属性
- PhoneNumberUtil 类:提供电话号码的解析和验证功能
parse方法:将输入的电话号码字符串解析为PhoneNumber对象,去除非数字字符isValidNumber方法:验证电话号码是否有效,这里简单检查是否是11位数字
开发注意事项
- 该实现是一个简化版本,仅用于演示目的
- 在实际应用中,建议使用真实的
dlibphonenumber库以获得更准确的验证结果 - 目前只支持中国大陆11位手机号码的验证
电话号码验证组件设计与实现
PhoneNumberValidatorWidget 是整个应用的核心组件,它实现了用户界面交互和验证逻辑的整合。
核心实现代码
import 'package:flutter/material.dart';
import 'dlibphonenumber.dart';
class PhoneNumberValidatorWidget extends StatefulWidget {
const PhoneNumberValidatorWidget({super.key});
State<PhoneNumberValidatorWidget> createState() => _PhoneNumberValidatorWidgetState();
}
class _PhoneNumberValidatorWidgetState extends State<PhoneNumberValidatorWidget> {
final TextEditingController _phoneController = TextEditingController();
String _validationResult = '';
String _carrierInfo = '';
String _countryCode = 'CN';
bool _isValid = false;
bool _isLoading = false;
// 示例电话号码
final List<String> _exampleNumbers = [
'13800138000',
'13912345678',
'15012345678',
'18612345678',
];
Future<void> _validatePhoneNumber() async {
if (_phoneController.text.isEmpty) {
setState(() {
_validationResult = '请输入电话号码';
_carrierInfo = '';
_isValid = false;
});
return;
}
setState(() {
_isLoading = true;
_validationResult = '验证中...';
_carrierInfo = '';
});
try {
final phoneNumber = await PhoneNumberUtil().parse(_phoneController.text, _countryCode);
final isValid = await PhoneNumberUtil().isValidNumber(phoneNumber);
setState(() {
if (isValid) {
_validationResult = '电话号码有效';
_isValid = true;
_getCarrierInfo(phoneNumber);
} else {
_validationResult = '电话号码无效';
_carrierInfo = '';
_isValid = false;
}
});
} catch (e) {
setState(() {
_validationResult = '验证失败: ${e.toString()}';
_carrierInfo = '';
_isValid = false;
});
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _getCarrierInfo(PhoneNumber phoneNumber) async {
try {
// 这里我们模拟获取运营商信息
// 在实际应用中,你可以使用其他库或API来获取真实的运营商信息
String carrier = '';
final nationalNumber = phoneNumber.nationalNumber;
if (nationalNumber.startsWith('138')) {
carrier = '中国移动';
} else if (nationalNumber.startsWith('139')) {
carrier = '中国移动';
} else if (nationalNumber.startsWith('150')) {
carrier = '中国移动';
} else if (nationalNumber.startsWith('186')) {
carrier = '中国联通';
} else if (nationalNumber.startsWith('189')) {
carrier = '中国电信';
} else {
carrier = '未知运营商';
}
setState(() {
_carrierInfo = '运营商: $carrier';
});
} catch (e) {
setState(() {
_carrierInfo = '获取运营商信息失败';
});
}
}
void _selectExampleNumber(String number) {
setState(() {
_phoneController.text = number;
_validationResult = '';
_carrierInfo = '';
_isValid = false;
});
}
void dispose() {
_phoneController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'电话号码验证与运营商信息',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 20.0),
TextField(
controller: _phoneController,
decoration: InputDecoration(
labelText: '请输入电话号码',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
_phoneController.clear();
setState(() {
_validationResult = '';
_carrierInfo = '';
_isValid = false;
});
},
),
),
keyboardType: TextInputType.phone,
),
const SizedBox(height: 20.0),
ElevatedButton(
onPressed: _isLoading ? null : _validatePhoneNumber,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 15.0),
),
child: _isLoading
? const SizedBox(
height: 20.0,
width: 20.0,
child: CircularProgressIndicator(
strokeWidth: 2.0,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: const Text('验证电话号码'),
),
const SizedBox(height: 20.0),
if (_validationResult.isNotEmpty)
Container(
padding: const EdgeInsets.all(15.0),
decoration: BoxDecoration(
color: _isValid ? Colors.green.shade100 : Colors.red.shade100,
borderRadius: BorderRadius.circular(8.0),
),
child: Text(
_validationResult,
style: TextStyle(
color: _isValid ? Colors.green.shade800 : Colors.red.shade800,
fontSize: 16.0,
),
textAlign: TextAlign.center,
),
),
if (_carrierInfo.isNotEmpty)
Container(
margin: const EdgeInsets.only(top: 10.0),
padding: const EdgeInsets.all(15.0),
decoration: BoxDecoration(
color: Colors.blue.shade100,
borderRadius: BorderRadius.circular(8.0),
),
child: Text(
_carrierInfo,
style: const TextStyle(
color: Colors.blue,
fontSize: 16.0,
),
textAlign: TextAlign.center,
),
),
const SizedBox(height: 30.0),
const Text(
'示例电话号码:',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10.0),
Wrap(
spacing: 10.0,
runSpacing: 10.0,
children: _exampleNumbers.map((number) {
return ElevatedButton(
onPressed: () => _selectExampleNumber(number),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey.shade100,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8.0),
),
child: Text(number),
);
}).toList(),
),
],
),
);
}
}
组件设计说明
- 状态管理:使用
StatefulWidget管理组件状态,包括电话号码输入、验证结果、运营商信息和加载状态 - 输入处理:提供
TextField用于电话号码输入,支持清空操作 - 验证逻辑:调用
PhoneNumberUtil进行电话号码验证,处理异步操作和错误 - 结果展示:根据验证结果显示不同样式的提示信息,有效为绿色,无效为红色
- 运营商信息:根据电话号码前缀识别运营商信息并展示
- 示例数据:提供示例电话号码,方便用户快速测试
使用方法
-
在需要使用电话号码验证的地方导入组件:
import 'phone_number_validator_widget.dart'; -
在布局中添加组件:
const PhoneNumberValidatorWidget()
开发注意事项
- 组件使用了
async/await处理异步操作,确保UI响应流畅 - 实现了完整的错误处理机制,提高应用的稳定性
- 使用了
TextField的keyboardType: TextInputType.phone提供更合适的键盘布局 - 注意在
dispose方法中释放TextEditingController,避免内存泄漏
应用入口与集成
在 main.dart 文件中,我们集成了 PhoneNumberValidatorWidget 组件,构建了完整的应用。
核心实现代码
import 'package:flutter/material.dart';
import 'phone_number_validator_widget.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter for openHarmony',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
debugShowCheckedModeBanner: false,
home: const MyHomePage(title: 'Flutter for openHarmony'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const PhoneNumberValidatorWidget(),
],
),
),
);
}
}
集成说明
- 导入组件:在
main.dart中导入phone_number_validator_widget.dart - 添加到布局:将
PhoneNumberValidatorWidget添加到MyHomePage的布局中 - 居中显示:使用
Center和Column确保组件在屏幕中居中显示 - 主题配置:使用
ThemeData配置应用主题,启用 Material 3
开发注意事项
- 移除了默认的计数器功能,专注于电话号码验证功能
- 使用
debugShowCheckedModeBanner: false移除调试横幅,提供更干净的界面 - 保持布局简洁,让用户专注于电话号码验证功能
本次开发中容易遇到的问题
-
电话号码验证逻辑的准确性
- 问题:不同国家和地区的电话号码格式差异很大,简单的长度检查可能不够准确
- 解决方案:在实际应用中,建议使用成熟的电话号码验证库,如
dlibphonenumber,它支持全球范围内的电话号码验证
-
异步操作的处理
- 问题:电话号码验证和运营商信息获取是异步操作,如果处理不当可能导致UI卡顿或状态不一致
- 解决方案:使用
async/await处理异步操作,并在操作过程中显示加载指示器,提升用户体验
-
运营商信息识别的完整性
- 问题:电话号码前缀与运营商的对应关系可能会随时间变化,硬编码的映射可能不够准确
- 解决方案:定期更新运营商前缀映射表,或使用第三方API获取实时的运营商信息
-
Flutter for OpenHarmony 的兼容性
- 问题:在将Flutter应用适配到OpenHarmony平台时,可能会遇到平台特定的API差异
- 解决方案:使用平台无关的Flutter API,避免直接调用平台特定的功能,确保跨平台兼容性
-
UI响应式设计
- 问题:在不同尺寸的设备上,UI布局可能会出现问题
- 解决方案:使用Flutter的响应式布局组件,如
Expanded、Flexible等,确保在不同设备上都能正常显示
-
内存管理
- 问题:在频繁的状态更新和异步操作中,可能会出现内存泄漏
- 解决方案:正确使用
dispose方法释放资源,特别是TextEditingController等需要手动管理的对象
总结本次开发中用到的技术点
-
Flutter 组件化开发
- 将电话号码验证功能封装成独立的
PhoneNumberValidatorWidget组件,提高代码复用性和可维护性 - 使用
StatefulWidget和StatelessWidget构建不同类型的组件
- 将电话号码验证功能封装成独立的
-
异步编程
- 使用
async/await处理电话号码验证和运营商信息获取的异步操作 - 实现了完善的异步错误处理机制
- 使用
-
状态管理
- 使用 Flutter 的
setState管理组件状态,包括验证结果、运营商信息、加载状态等 - 合理组织状态变量,确保状态更新的一致性
- 使用 Flutter 的
-
用户交互设计
- 添加了加载指示器、错误提示、示例电话号码选择等交互元素
- 实现了响应式按钮状态,在加载过程中禁用验证按钮
-
UI 布局
- 使用
Column、TextField、ElevatedButton、Wrap等 Flutter 组件构建美观的用户界面 - 应用了 Material 3 设计规范,提供现代化的视觉效果
- 使用
-
错误处理
- 实现了完善的错误处理机制,捕获并显示电话号码验证过程中的异常
- 对空输入、验证失败等情况进行了合理的提示
-
OpenHarmony 适配
- 确保代码在 OpenHarmony 平台上正常运行,实现跨平台兼容
- 遵循 OpenHarmony 的应用结构规范
-
工具类设计
- 实现了模拟的
PhoneNumberUtil工具类,提供电话号码解析和验证功能 - 设计了清晰的类结构和方法接口,便于后续扩展
- 实现了模拟的
通过本项目的开发,我们不仅实现了一个功能完整的电话号码验证工具,还掌握了 Flutter for OpenHarmony 开发的核心技术点,为跨生态应用开发积累了宝贵经验。
更多推荐
所有评论(0)