
在数字时代,人们越来越关注自我认知与情绪价值。无论是查看“今日运势”寻求心理慰藉,还是通过“性格测试”了解内在倾向,这类轻量级互动内容总能引发广泛共鸣。本文将带你深入剖析一款使用 Flutter 开发的趣味应用——《今日运势与心理测试》,从 UI 设计、状态管理到交互逻辑,全面解析如何用简洁代码构建一个兼具娱乐性与沉浸感的心灵小站。
完整效果展示


该应用包含两大核心功能模块:
整个应用采用 深色主题(Dark Mode),背景为 #121212,主色调为深紫色(deepPurple),营造出神秘、宁静又略带科技感的氛围,完美契合“命运”与“潜意识”的主题。
theme: ThemeData(
primaryColor: Colors.deepPurple,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple, brightness: Brightness.dark),
useMaterial3: true,
scaffoldBackgroundColor: const Color(0xFF121212),
)
#121212,减少视觉疲劳;grey[800]/grey[900] 等深灰层级,形成柔和对比,避免刺眼。12 个星座以 Wrap 组件排列,自动换行,适配不同屏幕宽度:
Wrap(
spacing: 8,
runSpacing: 8,
children: _zodiacs.map((zodiac) => ElevatedButton(...)).toList(),
)
每个按钮采用半透明深紫底色(opacity: 0.3)+ 白色文字 + 圆角设计,点击反馈清晰,整体如“星盘”般有序排列。
运势结果以卡片形式展示,核心亮点在于 线性进度条:
LinearProgressIndicator(
value: data['score'] / 100,
color: data['score'] > 60 ? Colors.green : Colors.red,
)
Icons.star,水瓶座用 Icons.bolt),强化星座符号联想。Card 中,问题加粗,选项灰色弱化,层次分明;虽然运势内容由随机数决定,但文案库经过精心设计:
final List<String> texts = [
'今天是行动的一天!大胆尝试新事物...',
'保持耐心。今天适合处理细节工作...',
'社交运极佳。你会遇到有趣的人...',
// ...
];
每条文案都包含 具体建议 + 情绪引导,让用户感觉“被理解”,而非冷冰冰的随机语句。
当前版本虽未真正记录用户选项(仅为演示),但其架构已预留扩展空间:
_quizQuestions 结构清晰,易于接入真实答题逻辑;_getPersonalityResult 返回四种典型人格画像,语言风格兼具专业性与亲和力,如: “你是一个极度理性的人……但也需要学会偶尔放松,感受生活的情感面。”
这种“肯定+建议”的句式,极易引发用户共鸣与分享欲。
_zodiacs、测试题目 _quizQuestions、运势文案 texts 均独立定义;_getZodiacIcon 封装在函数中,便于未来替换为自定义 SVG 或图片。_buildFortuneCard 方法,避免 build 函数臃肿;_currentFortune, _quizResult, _isTestCompleted)集中管理,逻辑清晰。Future.delayed(const Duration(seconds: 2), () {
// 更新结果
});通过延迟更新,模拟网络请求或 AI 计算过程,提升用户体验的真实感。
方向 | 建议 |
|---|---|
真实答题逻辑 | 记录用户选项,根据 A/B 选择计算性格维度(如内向/外向) |
每日缓存机制 | 使用 shared_preferences 缓存当日运势,避免重复生成 |
分享功能 | 添加“分享我的运势”按钮,生成图片或文本供社交传播 |
动画增强 | 为运势卡片添加淡入动画,为进度条添加数值滚动效果 |
这款《今日运势与心理测试》应用,虽仅数百行代码,却完整展现了 Flutter 在构建情感化、互动型应用上的独特优势。它没有复杂的业务逻辑,却通过精巧的 UI 设计、有温度的文案和恰到好处的交互反馈,成功营造出一种“数字占卜”的仪式感。
欢迎加入 开源鸿蒙跨平台开发者社区,获取最新资源与技术支持: 👉 开源鸿蒙跨平台开发者社区 完整代码展示
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(const FortuneApp());
}
class FortuneApp extends StatelessWidget {
const FortuneApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '今日运势',
theme: ThemeData(
primaryColor: Colors.deepPurple,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple, brightness: Brightness.dark),
useMaterial3: true,
scaffoldBackgroundColor: const Color(0xFF121212), // 深色背景
),
home: const FortuneHome(),
debugShowCheckedModeBanner: false,
);
}
}
class FortuneHome extends StatefulWidget {
const FortuneHome({super.key});
@override
State<FortuneHome> createState() => _FortuneHomeState();
}
class _FortuneHomeState extends State<FortuneHome> {
// 模拟的星座列表
final List<String> _zodiacs = [
'白羊座',
'金牛座',
'双子座',
'巨蟹座',
'狮子座',
'处女座',
'天秤座',
'天蝎座',
'射手座',
'摩羯座',
'水瓶座',
'双鱼座'
];
// 模拟的心理测试题目
final List<Map<String, dynamic>> _quizQuestions = [
{
'question': '在压力之下,你更倾向于:',
'choices': ['A. 寻找逻辑解决方案', 'B. 寻求朋友的情感支持']
},
{
'question': '周末你更愿意:',
'choices': ['A. 待在家里充电', 'B. 出去社交冒险']
},
{
'question': '面对未知,你的第一反应是:',
'choices': ['A. 兴奋和好奇', 'B. 谨慎和担忧']
},
];
// 测试结果
String _quizResult = '';
bool _isTestCompleted = false;
// 当前选中的星座运势
Map<String, dynamic>? _currentFortune;
// 随机数生成器
final Random _random = Random();
// 生成星座运势
void _generateFortune(String zodiac) {
setState(() {
_currentFortune = {
'name': zodiac,
'score': _random.nextInt(100),
'text': _getFortuneText(),
'icon': _getZodiacIcon(zodiac),
};
});
}
// 获取运势文案
String _getFortuneText() {
final List<String> texts = [
'今天是行动的一天!大胆尝试新事物,好运会眷顾勇敢者。',
'保持耐心。今天适合处理细节工作,不宜做重大决策。',
'社交运极佳。你会遇到有趣的人,或者收到意想不到的消息。',
'财运小爆发。可能会有额外的收入,但也别忘了储蓄。',
'注意休息。身体在向你发出信号,不要过度透支精力。',
'灵感迸发。你的创意想法今天会得到他人的高度认可。',
];
return texts[_random.nextInt(texts.length)];
}
// 获取星座图标 (使用Material Icon模拟)
IconData _getZodiacIcon(String zodiac) {
switch (zodiac) {
case '白羊座':
return Icons.whatshot;
case '金牛座':
return Icons.eco;
case '双子座':
return Icons.chat;
case '巨蟹座':
return Icons.heart_broken;
case '狮子座':
return Icons.star;
case '处女座':
return Icons.cleaning_services;
case '天秤座':
return Icons.scale;
case '天蝎座':
return Icons.visibility;
case '射手座':
return Icons.explore;
case '摩羯座':
return Icons.lunch_dining;
case '水瓶座':
return Icons.bolt;
case '双鱼座':
return Icons.opacity;
default:
return Icons.question_mark;
}
}
// 处理心理测试逻辑
void _startQuiz() {
setState(() {
_isTestCompleted = false;
_quizResult = '正在分析你的性格...';
// 模拟异步处理
Future.delayed(const Duration(seconds: 2), () {
final int random = _random.nextInt(4);
setState(() {
_quizResult = _getPersonalityResult(random);
_isTestCompleted = true;
});
});
});
}
String _getPersonalityResult(int index) {
switch (index) {
case 0:
return '【逻辑分析型】\n你是一个极度理性的人。在面对问题时,你习惯用逻辑和数据说话。你擅长规划,但也需要学会偶尔放松,感受生活的情感面。';
case 1:
return '【社交达人型】\n你充满活力,天生的社交家。你从与他人的互动中获取能量,善于沟通。注意在喧嚣中保持独立的思考。';
case 2:
return '【沉稳内敛型】\n你深思熟虑,是值得信赖的伙伴。你喜欢按部就班,厌恶风险。你的稳定是团队的基石,偶尔也可以尝试跳出舒适区。';
case 3:
return '【冒险探索型】\n你对世界充满好奇,渴望新鲜感。你适应力强,敢于挑战。记得在追求刺激的同时,做好风险评估。';
default:
return '【全能型】\n你拥有平衡的性格,能根据环境切换模式。这既是优点也是挑战,找到核心的自我会让你更强大。';
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('✨ 今日运势与测试'),
centerTitle: true,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// --- 星座运势模块 ---
const Text(
'🌟 点击查看星座运势',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Wrap(
spacing: 8,
runSpacing: 8,
children: _zodiacs.map((zodiac) {
return ElevatedButton(
onPressed: () => _generateFortune(zodiac),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepPurple.withOpacity(0.3),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 10),
),
child: Text(zodiac),
);
}).toList(),
),
// 星座结果展示
if (_currentFortune != null) _buildFortuneCard(_currentFortune!),
const SizedBox(height: 30),
// --- 心理测试模块 ---
const Text(
'🧠 心理小测试',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
const Text(
'回答以下3个问题,了解你的潜意识性格倾向:',
style: TextStyle(color: Colors.grey),
),
const SizedBox(height: 10),
..._quizQuestions.map((q) {
return Card(
color: Colors.grey[900],
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(q['question']!,
style: const TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 5),
...List.generate(q['choices']!.length, (index) {
return Text(
'${['A', 'B'][index]}. ${q['choices']![index]}',
style: const TextStyle(color: Colors.grey),
);
}),
],
),
),
);
}).toList(),
const SizedBox(height: 10),
Center(
child: ElevatedButton(
onPressed: _startQuiz,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
),
child: const Text('开始测试'),
),
),
// 测试结果展示
if (_quizResult.isNotEmpty)
Card(
color: Colors.grey[800],
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'📊 测试结果',
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
Text(
_quizResult,
style: const TextStyle(height: 1.6),
),
],
),
),
),
],
),
),
);
}
// 构建运势卡片组件
Widget _buildFortuneCard(Map<String, dynamic> data) {
return Card(
margin: const EdgeInsets.symmetric(vertical: 10),
color: Colors.purple.withOpacity(0.1),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(data['icon'], color: Colors.purple, size: 30),
const SizedBox(width: 10),
Text(
'${data['name']} 今日运势',
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 10),
LinearProgressIndicator(
value: data['score'] / 100,
backgroundColor: Colors.grey[800],
color: data['score'] > 60 ? Colors.green : Colors.red,
),
const SizedBox(height: 5),
Text(
'评分: ${data['score']}/100',
style: TextStyle(
color: data['score'] > 60 ? Colors.green : Colors.red),
),
const SizedBox(height: 10),
Text(
data['text'],
style: const TextStyle(height: 1.5),
),
],
),
),
);
}
}