欢迎加入开源鸿蒙跨平台社区: 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 进行电话号码验证,处理异步操作和错误
  • 结果展示:根据验证结果显示不同样式的提示信息,有效为绿色,无效为红色
  • 运营商信息:根据电话号码前缀识别运营商信息并展示
  • 示例数据:提供示例电话号码,方便用户快速测试

使用方法

  1. 在需要使用电话号码验证的地方导入组件:

    import 'phone_number_validator_widget.dart';
    
  2. 在布局中添加组件:

    const PhoneNumberValidatorWidget()
    

开发注意事项

  • 组件使用了 async/await 处理异步操作,确保UI响应流畅
  • 实现了完整的错误处理机制,提高应用的稳定性
  • 使用了 TextFieldkeyboardType: 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 的布局中
  • 居中显示:使用 CenterColumn 确保组件在屏幕中居中显示
  • 主题配置:使用 ThemeData 配置应用主题,启用 Material 3

开发注意事项

  • 移除了默认的计数器功能,专注于电话号码验证功能
  • 使用 debugShowCheckedModeBanner: false 移除调试横幅,提供更干净的界面
  • 保持布局简洁,让用户专注于电话号码验证功能

本次开发中容易遇到的问题

  1. 电话号码验证逻辑的准确性

    • 问题:不同国家和地区的电话号码格式差异很大,简单的长度检查可能不够准确
    • 解决方案:在实际应用中,建议使用成熟的电话号码验证库,如 dlibphonenumber,它支持全球范围内的电话号码验证
  2. 异步操作的处理

    • 问题:电话号码验证和运营商信息获取是异步操作,如果处理不当可能导致UI卡顿或状态不一致
    • 解决方案:使用 async/await 处理异步操作,并在操作过程中显示加载指示器,提升用户体验
  3. 运营商信息识别的完整性

    • 问题:电话号码前缀与运营商的对应关系可能会随时间变化,硬编码的映射可能不够准确
    • 解决方案:定期更新运营商前缀映射表,或使用第三方API获取实时的运营商信息
  4. Flutter for OpenHarmony 的兼容性

    • 问题:在将Flutter应用适配到OpenHarmony平台时,可能会遇到平台特定的API差异
    • 解决方案:使用平台无关的Flutter API,避免直接调用平台特定的功能,确保跨平台兼容性
  5. UI响应式设计

    • 问题:在不同尺寸的设备上,UI布局可能会出现问题
    • 解决方案:使用Flutter的响应式布局组件,如 ExpandedFlexible 等,确保在不同设备上都能正常显示
  6. 内存管理

    • 问题:在频繁的状态更新和异步操作中,可能会出现内存泄漏
    • 解决方案:正确使用 dispose 方法释放资源,特别是 TextEditingController 等需要手动管理的对象

总结本次开发中用到的技术点

  1. Flutter 组件化开发

    • 将电话号码验证功能封装成独立的 PhoneNumberValidatorWidget 组件,提高代码复用性和可维护性
    • 使用 StatefulWidgetStatelessWidget 构建不同类型的组件
  2. 异步编程

    • 使用 async/await 处理电话号码验证和运营商信息获取的异步操作
    • 实现了完善的异步错误处理机制
  3. 状态管理

    • 使用 Flutter 的 setState 管理组件状态,包括验证结果、运营商信息、加载状态等
    • 合理组织状态变量,确保状态更新的一致性
  4. 用户交互设计

    • 添加了加载指示器、错误提示、示例电话号码选择等交互元素
    • 实现了响应式按钮状态,在加载过程中禁用验证按钮
  5. UI 布局

    • 使用 ColumnTextFieldElevatedButtonWrap 等 Flutter 组件构建美观的用户界面
    • 应用了 Material 3 设计规范,提供现代化的视觉效果
  6. 错误处理

    • 实现了完善的错误处理机制,捕获并显示电话号码验证过程中的异常
    • 对空输入、验证失败等情况进行了合理的提示
  7. OpenHarmony 适配

    • 确保代码在 OpenHarmony 平台上正常运行,实现跨平台兼容
    • 遵循 OpenHarmony 的应用结构规范
  8. 工具类设计

    • 实现了模拟的 PhoneNumberUtil 工具类,提供电话号码解析和验证功能
    • 设计了清晰的类结构和方法接口,便于后续扩展

通过本项目的开发,我们不仅实现了一个功能完整的电话号码验证工具,还掌握了 Flutter for OpenHarmony 开发的核心技术点,为跨生态应用开发积累了宝贵经验。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐