【maaath】Flutter for OpenHarmony 短信管理应用实战

Flutter for OpenHarmony 短信管理应用实战

作者:maaath


欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net

前言

在移动应用开发领域,跨平台框架一直是开发者关注的焦点。Flutter 作为 Google 推出的 UI 框架,以其高效的渲染引擎和丰富的组件库著称。而 OpenHarmony 作为国产操作系统的新兴力量,正在吸引越来越多的开发者加入其生态建设。本文将通过一个实际的短信管理应用案例,探索 Flutter 在 OpenHarmony 平台上的开发实践,实现真正的跨平台开发。

项目概述

本次实践的短信管理应用是一个功能完整的移动应用,包含以下核心功能模块:

  • 会话管理:支持查看、搜索、筛选短信会话
  • 消息收发:模拟消息发送功能,支持送达状态展示
  • 模板管理:预设快捷回复模板,提高沟通效率
  • 垃圾拦截:基于关键词的智能垃圾短信过滤
  • 数据统计:实时展示短信数据的各项指标
  • 备份导出:支持数据的导出和导入

数据模型设计

良好的数据模型是应用架构的基础。在 Flutter 实现中,我们采用 Dart 语言定义数据类:

dart 复制代码
class SmsConversation {
  final String id;
  final String contactName;
  final String contactPhone;
  final String avatar;
  final String lastMessage;
  final int lastMessageTime;
  final int unreadCount;
  final bool isPinned;
  final bool isBlocked;
  final bool isFavorite;
  final List<SmsMessage> messages;

  SmsConversation({
    required this.id,
    required this.contactName,
    required this.contactPhone,
    required this.avatar,
    required this.lastMessage,
    required this.lastMessageTime,
    this.unreadCount = 0,
    this.isPinned = false,
    this.isBlocked = false,
    this.isFavorite = false,
    this.messages = const [],
  });
}

enum MessageStatus { sending, sent, delivered, failed }

enum MessageType { text, template, spam }

相比 ETS 的 interface 定义,Dart 的 class 提供了更强的面向对象特性,包括构造函数、getter/setter 以及方法定义。这种设计使得数据模型更加健壮,也便于后续的扩展和维护。

状态管理方案

在 Flutter 中,状态管理是开发中的核心环节。本项目采用 Provider 模式,这是一种轻量级且易于理解的状态管理方案:

dart 复制代码
class SmsViewModel extends ChangeNotifier {
  List<SmsConversation> _conversations = [];
  List<SmsTemplate> _templates = [];
  SmsSettings _settings = SmsSettings();
  
  List<SmsConversation> get conversations => _conversations;
  
  Future<void> loadConversations() async {
    // 从本地存储加载数据
    final prefs = await SharedPreferences.getInstance();
    final jsonStr = prefs.getString('conversations');
    if (jsonStr != null) {
      final List<dynamic> jsonList = json.decode(jsonStr);
      _conversations = jsonList.map((e) => SmsConversation.fromJson(e)).toList();
    } else {
      // 使用模拟数据
      _conversations = SmsMockData.generateMockConversations();
    }
    notifyListeners();
  }
  
  Future<void> sendMessage(String conversationId, String content) async {
    final message = SmsMessage(
      id: 'msg_${DateTime.now().millisecondsSinceEpoch}',
      conversationId: conversationId,
      content: content,
      timestamp: DateTime.now().millisecondsSinceEpoch,
      isIncoming: false,
      status: MessageStatus.sending,
    );
    
    // 更新本地状态
    final index = _conversations.indexWhere((c) => c.id == conversationId);
    if (index != -1) {
      _conversations[index].messages.insert(0, message);
      _conversations[index].lastMessage = content;
      _conversations[index].lastMessageTime = message.timestamp;
    }
    notifyListeners();
    
    // 模拟发送过程
    await Future.delayed(Duration(milliseconds: 500));
    message.status = MessageStatus.sent;
    notifyListeners();
    
    await Future.delayed(Duration(seconds: 1));
    message.status = MessageStatus.delivered;
    notifyListeners();
  }
}

这种设计将业务逻辑与 UI 分离,使得代码结构更加清晰。当数据发生变化时,通过 notifyListeners() 通知 UI 层更新界面,实现了响应式编程。

会话列表页面实现

会话列表是短信应用的核心页面之一。在 Flutter 中,我们使用 ListView.builder 实现高效的列表渲染:

dart 复制代码
class SmsListPage extends StatelessWidget {
  final List<SmsConversation> conversations;
  final Function(SmsConversation) onConversationTap;

  const SmsListPage({
    Key? key,
    required this.conversations,
    required this.onConversationTap,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // 分离置顶和非置顶会话
    final pinned = conversations.where((c) => c.isPinned).toList();
    final normal = conversations.where((c) => !c.isPinned).toList();

    return ListView.builder(
      itemCount: pinned.length + normal.length,
      itemBuilder: (context, index) {
        final conversation = index < pinned.length 
            ? pinned[index] 
            : normal[index - pinned.length];
        return _ConversationItem(
          conversation: conversation,
          onTap: () => onConversationTap(conversation),
        );
      },
    );
  }
}

class _ConversationItem extends StatelessWidget {
  final SmsConversation conversation;
  final VoidCallback onTap;

  const _ConversationItem({
    required this.conversation,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: Stack(
        children: [
          CircleAvatar(
            radius: 25,
            backgroundColor: Colors.grey[200],
            child: Text(conversation.avatar, style: TextStyle(fontSize: 20)),
          ),
          if (conversation.unreadCount > 0)
            Positioned(
              right: 0,
              top: 0,
              child: Container(
                padding: EdgeInsets.all(4),
                decoration: BoxDecoration(
                  color: Colors.red,
                  shape: BoxShape.circle,
                ),
                child: Text(
                  conversation.unreadCount > 99 
                      ? '99+' 
                      : conversation.unreadCount.toString(),
                  style: TextStyle(color: Colors.white, fontSize: 10),
                ),
              ),
            ),
        ],
      ),
      title: Row(
        children: [
          if (conversation.isPinned)
            Padding(
              padding: EdgeInsets.only(right: 4),
              child: Text('📌', style: TextStyle(fontSize: 12)),
            ),
          Expanded(
            child: Text(
              conversation.contactName,
              maxLines: 1,
              overflow: TextOverflow.ellipsis,
              style: TextStyle(
                fontWeight: conversation.unreadCount > 0 
                    ? FontWeight.bold 
                    : FontWeight.w500,
              ),
            ),
          ),
        ],
      ),
      subtitle: Text(
        conversation.lastMessage,
        maxLines: 1,
        overflow: TextOverflow.ellipsis,
        style: TextStyle(color: Colors.grey),
      ),
      trailing: Text(
        _formatTime(conversation.lastMessageTime),
        style: TextStyle(color: Colors.grey, fontSize: 12),
      ),
      onTap: onTap,
    );
  }

  String _formatTime(int timestamp) {
    final now = DateTime.now().millisecondsSinceEpoch;
    final diff = now - timestamp;
    final oneDay = 24 * 60 * 60 * 1000;

    if (diff < 60 * 1000) return '刚刚';
    if (diff < 60 * 60 * 1000) return '${(diff / 60000).floor()}分钟前';
    if (diff < oneDay) {
      final date = DateTime.fromMillisecondsSinceEpoch(timestamp);
      return '${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}';
    }
    return '${date.month}/${date.day}';
  }
}

Flutter 的 widget 组合特性在这里得到了充分体现。通过合理拆分组件,我们将复杂的列表项分解为可复用的子组件,既提高了代码的可读性,也便于后续的维护和测试。

垃圾短信拦截功能

智能拦截是现代短信应用的标配功能。在 Flutter 实现中,我们采用规则引擎的方式:

dart 复制代码
class SpamFilterService {
  final List<SpamRule> _rules = [];

  Future<void> loadRules() async {
    // 从存储加载规则
  }

  bool checkSpam(String content) {
    for (final rule in _rules) {
      if (!rule.isEnabled) continue;
      
      try {
        final regex = RegExp(rule.pattern);
        if (regex.hasMatch(content)) {
          return true;
        }
      } catch (e) {
        // 正则表达式无效时,使用简单匹配
        if (content.contains(rule.keyword)) {
          return true;
        }
      }
    }
    return false;
  }

  Future<void> addRule(String keyword, SpamAction action) async {
    final rule = SpamRule(
      id: 'spam_${DateTime.now().millisecondsSinceEpoch}',
      keyword: keyword,
      pattern: '.*$keyword.*',
      action: action,
      isEnabled: true,
    );
    _rules.add(rule);
    await _saveRules();
  }
}

这种设计允许用户自定义拦截规则,通过关键词匹配和正则表达式两种方式,灵活应对各类垃圾短信。

设置页面与数据持久化

应用设置页面的实现展示了 Flutter 在表单处理方面的便利性:

dart 复制代码
class SmsSettingsPage extends StatelessWidget {
  final SmsSettings settings;
  final Function(String, dynamic) onSettingChanged;

  const SmsSettingsPage({
    Key? key,
    required this.settings,
    required this.onSettingChanged,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('短信设置')),
      body: ListView(
        children: [
          _buildSection('基础设置', [
            _buildSwitchTile(
              '送达报告',
              '接收短信送达回执',
              Icons.check_circle_outline,
              settings.deliveryReport,
              (value) => onSettingChanged('deliveryReport', value),
            ),
            _buildSwitchTile(
              '自动备份提醒',
              '定期提醒备份短信',
              Icons.notifications_outlined,
              settings.enableBackupReminder,
              (value) => onSettingChanged('enableBackupReminder', value),
            ),
          ]),
          _buildSection('过滤设置', [
            _buildSwitchTile(
              '垃圾短信过滤',
              '自动识别并拦截垃圾短信',
              Icons.block,
              settings.enableSpamFilter,
              (value) => onSettingChanged('enableSpamFilter', value),
            ),
          ]),
        ],
      ),
    );
  }

  Widget _buildSection(String title, List<Widget> children) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: EdgeInsets.fromLTRB(16, 16, 16, 8),
          child: Text(title, style: TextStyle(color: Colors.grey)),
        ),
        ...children,
      ],
    );
  }
}

数据持久化使用 shared_preferences 包,这是 Flutter 中处理轻量级数据存储的标准方案。对于更复杂的数据结构,我们使用 JSON 序列化进行存储和读取。

截图运行验证

以下是应用在实际设备上的运行效果截图:

图1:会话列表页面

图2:短信详情页面

图3:设置页面

通过实际运行验证,应用在 OpenHarmony 设备上运行流畅,UI 响应及时,数据存储正常。Flutter 的热重载功能也大大提升了开发效率。

开发经验总结

1. 性能优化

Flutter 的列表渲染机制非常高效,但在处理大量数据时仍需注意:

  • 使用 ListView.builder 实现懒加载
  • 合理使用 const 构造函数减少重建
  • 对复杂组件进行适当的拆分和缓存

2. 平台差异处理

虽然 Flutter 强调跨平台,但在实际开发中仍需处理平台差异:

  • 鸿蒙系统的权限管理
  • 文件路径的差异
  • 本地存储的 API 差异

3. 调试技巧

利用 Flutter DevTools 可以方便地进行:

  • Widget 树检查
  • 性能分析
  • 网络请求监控
  • 日志查看

展望

Flutter for OpenHarmony 的生态正在快速发展。未来,我们可以期待:

  • 更多的原生插件支持
  • 更完善的性能优化
  • 更丰富的组件库

作为开发者,我们应该积极参与社区建设,分享开发经验,共同推动跨平台技术的发展。

代码仓库

本项目的完整代码已开源至 AtomGit 仓库:

https://atomgit.com/maaath/flutter_sms_manager

结语

通过本次实践,我们验证了 Flutter 在 OpenHarmony 平台上的可行性。虽然目前生态还在完善中,但 Flutter 带来的开发效率提升和代码复用优势是显而易见的。希望本文能为正在进行跨平台开发的开发者提供一些参考,也欢迎大家交流讨论,共同探索更好的解决方案。


相关推荐
程序猿追2 小时前
从零打造一个“跳一跳”:在HarmonyOS模拟器上用Canvas复刻经典
华为·harmonyos
@不误正业2 小时前
第13章-开源鸿蒙是否适合做端侧AI操作系统
人工智能·开源·harmonyos
UnicornDev2 小时前
【HarmonyOS 6】底部悬浮导航的迷你栏适配(API23)
华为·harmonyos·arkts·鸿蒙
@不误正业2 小时前
OpenHarmony-A2A协议实战-多智能体跨应用协同架构与实现
人工智能·架构·harmonyos·开源鸿蒙
空中海2 小时前
iOS 静态逆向、IPA 结构与 Mach-O 分析
ios·华为·harmonyos
李李李勃谦2 小时前
鸿蒙PC文件加密工具实战:AES-256-GCM 加密
华为·harmonyos
maaath2 小时前
【maaath】Flutter for OpenHarmony打造跨平台便签备忘录应用
flutter·华为·harmonyos
千码君20162 小时前
flutter:与Android Studio模拟器的调试分享
android·flutter
李李李勃谦3 小时前
鸿蒙PC思维导图工具实战:拖拽交互与多格式导出
华为·交互·harmonyos