Flutter for OpenHarmony 实战_随机抽签应用设计与实现
摘要 本文介绍了使用Flutter开发OpenHarmony随机抽签应用的全过程。重点内容包括:1) 实现基础随机选择、多次抽签和权重抽签算法;2) 候选人管理功能,包括添加、批量导入和删除;3) 抽签动画效果,通过2秒滚动制造悬念;4) 结果展示和历史记录功能;5) UI界面设计要点。应用支持单次和多次抽签,具有实用性和趣味性,适用于抽奖、分组等场景。文章提供了完整的代码示例和界面截图,可作为跨
Flutter for OpenHarmony 实战:随机抽签应用设计与实现
欢迎加入开源鸿蒙跨平台社区:开源鸿蒙跨平台开发者社区
文章目录
前言
随机抽签应用是一个实用的工具类应用,广泛应用于抽奖、分组、决策等场景。本文将详细介绍随机抽签算法、候选人管理、动画效果实现、结果历史记录以及UI交互设计。
一、抽签算法实现
1.1 基础随机选择
class LotterySystem {
List<String> candidates = [];
String drawWinner() {
if (candidates.isEmpty) {
return '';
}
final random = Random();
final index = random.nextInt(candidates.length);
return candidates[index];
}
}
使用Random().nextInt()生成随机索引,从候选人列表中选择一个。
1.2 多次抽签
List<String> drawMultiple(int count) {
if (count > candidates.length) {
throw Exception('抽签数量超过候选人数量');
}
final selected = <String>[];
final available = List<String>.from(candidates);
for (int i = 0; i < count; i++) {
final random = Random();
final index = random.nextInt(available.length);
selected.add(available[index]);
available.removeAt(index);
}
return selected;
}
支持一次抽取多个不重复的候选人。
1.3 权重抽签
class WeightedCandidate {
final String name;
final int weight;
WeightedCandidate({required this.name, required this.weight});
}
String drawWeighted(List<WeightedCandidate> candidates) {
final totalWeight = candidates.fold(0, (sum, c) => sum + c.weight);
final random = Random();
final randomValue = random.nextInt(totalWeight);
int currentWeight = 0;
for (var candidate in candidates) {
currentWeight += candidate.weight;
if (randomValue < currentWeight) {
return candidate.name;
}
}
return candidates.last.name;
}
根据权重进行抽签,权重越高被抽中的概率越大。
二、候选人管理
2.1 添加候选人


void addCandidate(String name) {
if (name.trim().isEmpty) {
return;
}
if (candidates.contains(name)) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('该候选人已存在')),
);
return;
}
setState(() {
candidates.add(name);
});
}
添加候选人时检查重复和空名称。
2.2 批量导入
void importCandidates(String text) {
final lines = text.split('\n');
int added = 0;
for (var line in lines) {
final name = line.trim();
if (name.isNotEmpty && !candidates.contains(name)) {
candidates.add(name);
added++;
}
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('成功导入 $added 个候选人')),
);
}
支持从文本批量导入候选人,每行一个。
2.3 删除候选人

void removeCandidate(String name) {
setState(() {
candidates.remove(name);
});
}
void clearAll() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('清空候选人'),
content: const Text('确定要清空所有候选人吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
setState(() {
candidates.clear();
});
Navigator.pop(context);
},
child: const Text('确定'),
),
],
),
);
}
提供单个删除和清空全部功能。
三、抽签动画

3.1 滚动动画
String currentDisplay = '';
bool isAnimating = false;
List<String> history = [];
void startDrawing() async {
if (candidates.isEmpty || isAnimating) {
return;
}
isAnimating = true;
final duration = const Duration(milliseconds: 2000);
final start = DateTime.now();
while (DateTime.now().difference(start) < duration) {
final random = Random();
currentDisplay = candidates[random.nextInt(candidates.length)];
setState(() {});
await Future.delayed(const Duration(milliseconds: 50));
}
// 最终结果
final winner = drawWinner();
currentDisplay = winner;
history.add(winner);
isAnimating = false;
setState(() {});
showResultDialog(winner);
}
2秒内快速滚动显示候选人名称,制造悬念。
3.2 结果对话框

void showResultDialog(String winner) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('抽签结果'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.celebration, size: 64, color: Colors.amber),
const SizedBox(height: 16),
Text(
winner,
style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('确定'),
),
],
),
);
}
使用对话框展示最终结果,带有庆祝图标。
四、UI界面设计
4.1 主界面布局

Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('随机抽签'),
actions: [
IconButton(icon: const Icon(Icons.history), onPressed: showHistory),
IconButton(icon: const Icon(Icons.settings), onPressed: showSettings),
],
),
body: Column(
children: [
Expanded(
child: Center(
child: Text(
currentDisplay,
style: Theme.of(context).textTheme.displayLarge,
),
),
),
_buildCandidateList(),
_buildControlButtons(),
],
),
);
}
中心显示当前抽签结果,下方是候选人列表和控制按钮。
4.2 候选人列表
Widget _buildCandidateList() {
return Container(
height: 200,
padding: const EdgeInsets.all(16),
child: ListView.builder(
itemCount: candidates.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(candidates[index]),
trailing: IconButton(
icon: const Icon(Icons.remove_circle),
onPressed: () => removeCandidate(candidates[index]),
),
);
},
),
);
}
可滚动的候选人列表,每个项带删除按钮。
4.3 控制按钮
Widget _buildControlButtons() {
return Padding(
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton.icon(
onPressed: isAnimating ? null : startDrawing,
icon: const Icon(Icons.play_arrow),
label: const Text('开始抽签'),
),
ElevatedButton.icon(
onPressed: () => addCandidateDialog(),
icon: const Icon(Icons.add),
label: const Text('添加候选人'),
),
],
),
);
}
开始抽签和添加候选人两个主要按钮。
五、历史记录
5.1 记录存储
List<String> history = [];
List<DateTime> timestamps = [];
void addToHistory(String winner) {
history.add(winner);
timestamps.add(DateTime.now());
}
记录抽签结果和时间戳。
5.2 历史显示
void showHistory() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('抽签历史'),
content: SizedBox(
width: double.maxFinite,
height: 400,
child: ListView.builder(
itemCount: history.length,
itemBuilder: (context, index) {
final reversedIndex = history.length - 1 - index;
return ListTile(
title: Text(history[reversedIndex]),
subtitle: Text(_formatTime(timestamps[reversedIndex])),
leading: Text('${reversedIndex + 1}'),
);
},
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('关闭'),
),
TextButton(
onPressed: () {
setState(() {
history.clear();
timestamps.clear();
});
Navigator.pop(context);
},
child: const Text('清空历史'),
),
],
),
);
}
显示所有历史记录,最新记录在前。
六、设置功能
6.1 动画时长
int animationDuration = 2000; // 毫秒
void updateDuration(int duration) {
setState(() {
animationDuration = duration;
});
}
允许用户调整抽签动画时长。
6.2 音效开关
bool soundEnabled = true;
void toggleSound() {
setState(() {
soundEnabled = !soundEnabled;
});
}
控制抽签时是否播放音效。
七、数据持久化
7.1 保存数据
Future<void> saveData() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setStringList('candidates', candidates);
await prefs.setStringList('history', history);
}
使用SharedPreferences保存候选人和历史记录。
7.2 加载数据
Future<void> loadData() async {
final prefs = await SharedPreferences.getInstance();
final savedCandidates = prefs.getStringList('candidates');
final savedHistory = prefs.getStringList('history');
if (savedCandidates != null) {
setState(() {
candidates = savedCandidates;
});
}
if (savedHistory != null) {
history = savedHistory;
}
}
应用启动时自动加载数据。
总结
本文详细介绍了随机抽签应用的设计与实现。从随机算法到候选人管理,从动画效果到UI设计,每个技术点都直接影响应用的功能性和用户体验。通过这些技术的综合应用,实现了功能完整且实用的随机抽签应用。
更多推荐



所有评论(0)