Flutter for OpenHarmony 实战:投票管理系统完整开发指南

Flutter for OpenHarmony 实战:投票管理系统完整开发指南

文章目录

  • [Flutter for OpenHarmony 实战:投票管理系统完整开发指南](#Flutter for OpenHarmony 实战:投票管理系统完整开发指南)
    • 摘要
    • 一、项目背景与功能概述
      • [1.1 投票系统的应用场景](#1.1 投票系统的应用场景)
      • [1.2 应用功能规划](#1.2 应用功能规划)
      • [1.3 投票规则说明](#1.3 投票规则说明)
    • 二、投票系统设计原则
      • [2.1 用户界面设计](#2.1 用户界面设计)
      • [2.2 数据流向设计](#2.2 数据流向设计)
    • 三、技术选型与架构设计
      • [3.1 核心技术栈](#3.1 核心技术栈)
      • [3.2 应用架构](#3.2 应用架构)
      • [3.3 状态管理设计](#3.3 状态管理设计)
    • 四、数据模型设计
      • [4.1 候选人数据类](#4.1 候选人数据类)
      • [4.2 选择状态管理](#4.2 选择状态管理)
    • 五、候选人管理界面
      • [5.1 界面布局设计](#5.1 界面布局设计)
      • [5.2 候选人解析逻辑](#5.2 候选人解析逻辑)
      • [5.3 取消按钮功能](#5.3 取消按钮功能)
      • [5.4 投票规则说明](#5.4 投票规则说明)
    • 六、投票界面实现
      • [6.1 候选人列表](#6.1 候选人列表)
      • [6.2 复选框状态管理](#6.2 复选框状态管理)
    • 七、统计信息展示
      • [7.1 统计卡片设计](#7.1 统计卡片设计)
      • [7.2 统计信息布局](#7.2 统计信息布局)
      • [7.3 操作按钮区域](#7.3 操作按钮区域)
    • 八、完整代码实现
      • [8.1 投票提交逻辑](#8.1 投票提交逻辑)
      • [8.2 排序功能](#8.2 排序功能)
      • [8.3 刷新重置功能](#8.3 刷新重置功能)
    • 九、运行效果与测试
      • [9.1 项目运行命令](#9.1 项目运行命令)
      • [9.2 功能测试清单](#9.2 功能测试清单)
    • 十、总结
    • 十、总结

摘要

投票管理系统是各类组织和活动中必不可少的应用工具。本文将详细介绍如何使用Flutter for OpenHarmony框架开发一款功能完整的投票管理系统。文章涵盖了复选框状态管理、选票验证逻辑、实时统计、数据排序等核心技术点。通过本文学习,读者将掌握Flutter在鸿蒙平台上开发交互式应用的方法,了解如何构建功能完善、用户友好的投票应用。


一、项目背景与功能概述

1.1 投票系统的应用场景

投票系统广泛应用于:

  • 班级干部选举
  • 公司决策投票
  • 活动评选
  • 意见调查
  • 民主评议

1.2 应用功能规划

功能模块 具体功能
候选人管理 输入候选人名单、确认、取消
投票功能 复选框选择、确定投票、刷新重置
选票验证 最多选3人、超过作废、不选为弃权
实时统计 总票数、有效票、废票、弃权票
排序功能 按票数排序显示候选人

1.3 投票规则说明

  • 每张选票最多可选择3位候选人
  • 选择超过3人,该票作废
  • 不选择任何候选人,视为弃权
  • 点击复选框选中/取消选中候选人

二、投票系统设计原则

2.1 用户界面设计

简洁性原则

  • 候选人设置阶段:清晰的多行输入框
  • 投票阶段:直观的复选框列表
  • 统计阶段:醒目的统计卡片

一致性原则

  • 颜色编码:绿色-有效票、红色-废票、灰色-弃权
  • 按钮样式统一
  • 交互反馈一致

2.2 数据流向设计


三、技术选型与架构设计

3.1 核心技术栈

状态管理

  • StatefulWidget管理组件状态
  • setState更新UI
  • Set数据结构存储选中状态

UI组件

  • TextField:候选人输入
  • CheckboxListTile:复选框列表
  • Card:统计卡片展示
  • ListView:候选人列表

数据处理

  • 正则表达式解析候选人
  • 集合操作处理选择状态
  • 排序算法实现票数排序

3.2 应用架构

复制代码
VotingSystemApp (应用根组件)
    └── VotingSystemPage (投票系统页面)
        ├── AppBar (导航栏 + 刷新按钮)
        ├── 候选人设置界面
        │   ├── 输入提示
        │   ├── 多行输入框
        │   ├── 确认/取消按钮
        │   └── 投票规则说明
        └── 投票界面
            ├── 候选人列表(复选框)
            └── 统计信息区域
                ├── 统计卡片(4个)
                └── 操作按钮(投票、排序)

3.3 状态管理设计

dart 复制代码
class _VotingSystemPageState extends State<VotingSystemPage> {
  final List<Candidate> _candidates = [];           // 候选人列表
  final Set<String> _selectedCandidates = {};       // 选中的候选人
  final TextEditingController _controller = TextEditingController();

  int _totalVotes = 0;      // 总选票数
  int _invalidVotes = 0;    // 废票数
  int _abstainedVotes = 0;  // 弃权票数

  bool _isVotingMode = false;  // 是否处于投票模式
}

四、数据模型设计

4.1 候选人数据类

dart 复制代码
class Candidate {
  String name;  // 候选人姓名
  int votes;    // 得票数

  Candidate({required this.name, this.votes = 0});
}

设计要点

  • name:候选人唯一标识
  • votes:初始值为0,投票时累加

4.2 选择状态管理

使用Set数据结构存储选中的候选人:

dart 复制代码
final Set<String> _selectedCandidates = {};

Set的优势

  • 自动去重
  • 高效查找
  • 方便添加/删除

五、候选人管理界面

5.1 界面布局设计

dart 复制代码
Widget _buildSetupInterface() {
  return Padding(
    padding: const EdgeInsets.all(24),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        const Text('设置候选人名单'),
        const SizedBox(height: 16),
        const Text('请输入候选人姓名,用逗号或空格分隔'),
        const SizedBox(height: 16),
        TextField(
          controller: _candidatesController,
          maxLines: 5,
          decoration: const InputDecoration(
            labelText: '候选人名单',
            hintText: '例如:张三, 李四, 王五, 赵六',
            border: OutlineInputBorder(),
          ),
        ),
        // 按钮...
      ],
    ),
  );
}

5.2 候选人解析逻辑

dart 复制代码
void _confirmCandidates() {
  final text = _candidatesController.text.trim();
  if (text.isEmpty) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('请输入候选人名单')),
    );
    return;
  }

  // 按逗号或空格分隔
  final names = text.split(RegExp('[,,\\s]+'));
  final validNames = names.where((name) => name.trim().isNotEmpty).toList();

  if (validNames.isEmpty) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('候选人名单不能为空')),
    );
    return;
  }

  setState(() {
    _candidates.clear();
    for (var name in validNames) {
      _candidates.add(Candidate(name: name.trim()));
    }
    _isVotingMode = true;
    _resetStatistics();
  });
}

解析规则

  • 支持中英文逗号分隔
  • 支持空格分隔
  • 自动过滤空字符串
  • 去除首尾空格

5.3 取消按钮功能

dart 复制代码
void _cancelCandidates() {
  _candidatesController.clear();
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(content: Text('已清空输入')),
  );
}

5.4 投票规则说明

使用Card组件展示投票规则:

dart 复制代码
Card(
  color: Colors.blue.shade50,
  child: Padding(
    padding: const EdgeInsets.all(16),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            Icon(Icons.info_outline, color: Colors.blue),
            const SizedBox(width: 8),
            const Text('投票规则'),
          ],
        ),
        const SizedBox(height: 12),
        const Text('• 每张选票最多可选3人'),
        const Text('• 选择超过3人视为废票'),
        const Text('• 不选择任何候选人视为弃权'),
        const Text('• 点击复选框进行投票'),
      ],
    ),
  ),
)

六、投票界面实现

6.1 候选人列表

使用ListView.builder + CheckboxListTile:

dart 复制代码
Expanded(
  child: ListView.builder(
    padding: const EdgeInsets.all(16),
    itemCount: _candidates.length,
    itemBuilder: (context, index) {
      final candidate = _candidates[index];
      final isSelected = _selectedCandidates.contains(candidate.name);

      return Card(
        margin: const EdgeInsets.only(bottom: 12),
        child: CheckboxListTile(
          title: Text(
            candidate.name,
            style: const TextStyle(fontSize: 18),
          ),
          subtitle: Text('得票: ${candidate.votes}'),
          value: isSelected,
          onChanged: (bool? value) {
            setState(() {
              if (value == true) {
                _selectedCandidates.add(candidate.name);
              } else {
                _selectedCandidates.remove(candidate.name);
              }
            });
          },
          controlAffinity: ListTileControlAffinity.leading,
          contentPadding: const EdgeInsets.all(16),
        ),
      );
    },
  ),
)

CheckboxListTile属性

  • title:候选人姓名
  • subtitle:当前得票数
  • value:是否选中
  • onChanged:选中状态变化回调
  • controlAffinity:复选框位置(左侧)

6.2 复选框状态管理

dart 复制代码
onChanged: (bool? value) {
  setState(() {
    if (value == true) {
      _selectedCandidates.add(candidate.name);
    } else {
      _selectedCandidates.remove(candidate.name);
    }
  });
}

状态更新流程

  1. 用户点击复选框
  2. 触发onChanged回调
  3. 更新_selectedCandidates集合
  4. 调用setState刷新UI
  5. 重新渲染显示新状态

七、统计信息展示

7.1 统计卡片设计

dart 复制代码
Widget _buildStatCard(String title, String value, Color color) {
  return Expanded(
    child: Card(
      color: color,
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Column(
          children: [
            Text(
              title,
              style: const TextStyle(
                fontSize: 12,
                color: Colors.white,
              ),
            ),
            const SizedBox(height: 4),
            Text(
              value,
              style: const TextStyle(
                fontSize: 24,
                fontWeight: FontWeight.bold,
                color: Colors.white,
              ),
            ),
          ],
        ),
      ),
    ),
  );
}

7.2 统计信息布局

dart 复制代码
Row(
  children: [
    _buildStatCard('总选票', '$_totalVotes', Colors.blue),
    const SizedBox(width: 12),
    _buildStatCard('有效票', '${_totalVotes - _invalidVotes - _abstainedVotes}', Colors.green),
    const SizedBox(width: 12),
    _buildStatCard('废票', '$_invalidVotes', Colors.red),
    const SizedBox(width: 12),
    _buildStatCard('弃权', '$_abstainedVotes', Colors.grey),
  ],
)

颜色编码

  • 蓝色:总选票
  • 绿色:有效票
  • 红色:废票
  • 灰色:弃权票

7.3 操作按钮区域

dart 复制代码
Row(
  children: [
    Expanded(
      child: ElevatedButton.icon(
        onPressed: _submitVote,
        icon: const Icon(Icons.how_to_vote),
        label: const Text('确定投票'),
        style: ElevatedButton.styleFrom(
          padding: const EdgeInsets.symmetric(vertical: 12),
          backgroundColor: Colors.green,
        ),
      ),
    ),
    const SizedBox(width: 12),
    Expanded(
      child: ElevatedButton.icon(
        onPressed: _sortByVotes,
        icon: const Icon(Icons.sort),
        label: const Text('按票数排序'),
        style: ElevatedButton.styleFrom(
          padding: const EdgeInsets.symmetric(vertical: 12),
        ),
      ),
    ),
  ],
)

八、完整代码实现

8.1 投票提交逻辑

dart 复制代码
void _submitVote() {
  // 情况1:弃权票
  if (_selectedCandidates.isEmpty) {
    setState(() {
      _abstainedVotes++;
      _totalVotes++;
      _selectedCandidates.clear();
    });

    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('弃权票已记录')),
    );
    return;
  }

  // 情况2:废票(选择超过3人)
  if (_selectedCandidates.length > 3) {
    setState(() {
      _invalidVotes++;
      _totalVotes++;
      _selectedCandidates.clear();
    });

    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: Text('选择超过3人,该票作废'),
        backgroundColor: Colors.red,
      ),
    );
    return;
  }

  // 情况3:有效票
  setState(() {
    for (var candidate in _candidates) {
      if (_selectedCandidates.contains(candidate.name)) {
        candidate.votes++;
      }
    }
    _totalVotes++;
    _selectedCandidates.clear();
  });

  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(content: Text('投票成功')),
  );
}

验证流程

  1. 检查是否为空(弃权)
  2. 检查是否超过3人(废票)
  3. 为选中的候选人增加票数
  4. 清空选择状态

8.2 排序功能

dart 复制代码
void _sortByVotes() {
  setState(() {
    _candidates.sort((a, b) => b.votes.compareTo(a.votes));
  });

  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(content: Text('已按票数排序')),
  );
}

排序规则

  • 按票数降序排列
  • 票数相同时保持原有顺序

8.3 刷新重置功能

dart 复制代码
void _refreshSystem() {
  setState(() {
    _candidates.clear();
    _selectedCandidates.clear();
    _candidatesController.clear();
    _isVotingMode = false;
    _resetStatistics();
  });

  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(content: Text('系统已重置')),
  );
}

void _resetStatistics() {
  _totalVotes = 0;
  _invalidVotes = 0;
  _abstainedVotes = 0;
  for (var candidate in _candidates) {
    candidate.votes = 0;
  }
}

九、运行效果与测试

9.1 项目运行命令

bash 复制代码
cd E:\HarmonyOS\oh.code\voting_system
flutter run -d ohos

9.2 功能测试清单

候选人设置测试

  • 输入单个候选人
  • 输入多个候选人(逗号分隔)
  • 输入多个候选人(空格分隔)
  • 混合分隔符输入
  • 空输入验证
  • 取消按钮清空输入

投票功能测试

  • 选中单个候选人
  • 选中多个候选人(1-3人)
  • 选中超过3人(废票)
  • 不选中任何候选人(弃权)

统计功能测试

  • 有效票正确统计
  • 废票正确统计
  • 弃权票正确统计
  • 总票数正确计算

排序功能测试

  • 按票数降序排列
  • 排序后票数正确显示

重置功能测试

  • 刷新后回到设置界面
  • 所有数据清空

十、总结

本文详细介绍了使用Flutter for OpenHarmony开发投票管理系统的完整过程,涵盖了以下核心技术点:

  1. 复选框状态管理:CheckboxListTile + Set数据结构
  2. 选票验证逻辑:三层判断(弃权、废票、有效票)
  3. 实时统计机制:动态更新票数统计
  4. 排序算法:列表排序实现
  5. UI交互设计:直观的操作界面

这个项目展示了Flutter在交互式应用开发中的完整流程,代码结构清晰,功能完整。读者可以基于此项目添加更多功能,如:

  • 候选人图片显示
  • 投票时间限制
  • 历史记录查看
  • 导出投票结果
  • 多轮投票支持

通过本文的学习,读者应该能够独立开发类似的交互式应用,掌握Flutter在鸿蒙平台上的状态管理和UI交互技巧。


欢迎加入开源鸿蒙跨平台社区 : 开源鸿蒙跨平台开发者社区

  • 不选中任何候选人(弃权)

统计功能测试

  • 有效票正确统计
  • 废票正确统计
  • 弃权票正确统计
  • 总票数正确计算

排序功能测试

  • 按票数降序排列
  • 排序后票数正确显示

重置功能测试

  • 刷新后回到设置界面
  • 所有数据清空

十、总结

本文详细介绍了使用Flutter for OpenHarmony开发投票管理系统的完整过程,涵盖了以下核心技术点:

  1. 复选框状态管理:CheckboxListTile + Set数据结构
  2. 选票验证逻辑:三层判断(弃权、废票、有效票)
  3. 实时统计机制:动态更新票数统计
  4. 排序算法:列表排序实现
  5. UI交互设计:直观的操作界面

这个项目展示了Flutter在交互式应用开发中的完整流程,代码结构清晰,功能完整。读者可以基于此项目添加更多功能,如:

  • 候选人图片显示
  • 投票时间限制
  • 历史记录查看
  • 导出投票结果
  • 多轮投票支持

通过本文的学习,读者应该能够独立开发类似的交互式应用,掌握Flutter在鸿蒙平台上的状态管理和UI交互技巧。


欢迎加入开源鸿蒙跨平台社区 : 开源鸿蒙跨平台开发者社区

相关推荐
前端不太难34 分钟前
Flutter 如何设计可长期维护的模块边界?
flutter
小蜜蜂嗡嗡2 小时前
flutter列表中实现置顶动画
flutter
始持2 小时前
第十二讲 风格与主题统一
前端·flutter
始持2 小时前
第十一讲 界面导航与路由管理
flutter·vibecoding
始持2 小时前
第十三讲 异步操作与异步构建
前端·flutter
新镜3 小时前
【Flutter】 视频视频源横向、竖向问题
flutter
黄林晴3 小时前
Compose Multiplatform 1.10 发布:统一 Preview、Navigation 3、Hot Reload 三箭齐发
android·flutter
Swift社区4 小时前
Flutter 应该按功能拆,还是按技术层拆?
flutter
肠胃炎4 小时前
树形选择器组件封装
前端·flutter
程序员老刘19 小时前
跨平台开发地图:金三银四你准备好了吗? | 2026年3月
flutter·客户端