Flutter 框架跨平台鸿蒙开发 - 声音明信片
运行效果图声音明信片是一款将环境音录制并制作成精美明信片的创意应用。用户可以录制身边的声音——海浪、鸟鸣、城市喧嚣等,配上主题和文字,制作成独一无二的"声音明信片",寄给远方的朋友。这种新颖的表达方式,让声音跨越距离,传递情感。用声音记录美好,让思念有温度。声音明信片应用通过创新的"声音+明信片"概念,为用户提供了一种新颖的情感表达方式。波形可视化:实时显示录音波形精美主题:6种渐变主题配色明信片
·
声音明信片应用
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
一、项目概述
运行效果图


1.1 应用简介
声音明信片是一款将环境音录制并制作成精美明信片的创意应用。用户可以录制身边的声音——海浪、鸟鸣、城市喧嚣等,配上主题和文字,制作成独一无二的"声音明信片",寄给远方的朋友。这种新颖的表达方式,让声音跨越距离,传递情感。
应用核心理念:用声音记录美好,让思念有温度。
1.2 核心功能
| 功能模块 | 功能描述 | 实现方式 |
|---|---|---|
| 声音录制 | 模拟录音、波形可视化 | Timer + CustomPainter |
| 波形显示 | 实时波形动画 | CustomPainter绘制 |
| 主题选择 | 6种精美主题 | 渐变配色方案 |
| 明信片预览 | 实时预览效果 | 渐变容器 + 波形 |
| 寄送功能 | 标记寄出状态 | 状态管理 |
| 历史记录 | 明信片列表管理 | 内存存储 |
1.3 主题配色
| 主题 | 配色方案 | 适用场景 |
|---|---|---|
| 日落 | #FF6B6B → #FFE66D | 黄昏、温暖 |
| 海洋 | #4ECDC4 → #556270 | 海边、宁静 |
| 森林 | #134E5E → #71B280 | 自然、清新 |
| 星空 | #0F2027 → #203A43 | 夜晚、神秘 |
| 城市 | #373B44 → #4286f4 | 都市、现代 |
| 花园 | #FFECD2 → #FCB69F | 春天、浪漫 |
1.4 技术栈
| 技术领域 | 技术选型 | 版本要求 |
|---|---|---|
| 开发框架 | Flutter | >= 3.0.0 |
| 编程语言 | Dart | >= 2.17.0 |
| 设计规范 | Material Design 3 | - |
| 状态管理 | setState | - |
| 动画控制 | AnimationController | - |
| 自定义绘制 | CustomPainter | - |
| 目标平台 | 鸿蒙OS | API 21+ |
二、项目结构
lib/
├── main_sound_postcard.dart # 应用主入口(~800行)
│ ├── SoundPostcardApp # 根应用组件
│ ├── Postcard # 明信片数据模型
│ ├── WaveformPainter # 波形绘制器
│ └── SoundPostcardHomePage # 主页面
三、数据模型
3.1 Postcard 模型
class Postcard {
final String id; // 唯一标识(时间戳)
String title; // 明信片标题
String message; // 寄语内容
String location; // 录制地点
String theme; // 主题名称
int duration; // 录音时长(毫秒)
List<double> waveformData; // 波形数据
final DateTime createdAt; // 创建时间
bool isSent; // 是否已寄出
Postcard({
required this.id,
required this.title,
required this.message,
required this.location,
required this.theme,
required this.duration,
required this.waveformData,
required this.createdAt,
this.isSent = false,
});
}
四、核心功能实现
4.1 录音模拟
由于是模拟录音,使用Timer生成随机波形数据:
void _startRecording() {
setState(() {
_isRecording = true;
_recordingDuration = 0;
_currentWaveform = [];
});
_simulateRecording();
}
void _simulateRecording() {
Future.doWhile(() async {
if (!_isRecording) return false;
await Future.delayed(const Duration(milliseconds: 100));
if (!_isRecording) return false;
setState(() {
_recordingDuration += 100;
_currentWaveform.add(Random().nextDouble() * 0.8 + 0.2);
if (_currentWaveform.length > 50) {
_currentWaveform.removeAt(0);
}
});
return _recordingDuration < 10000; // 最长10秒
}).then((_) {
if (_isRecording) {
_stopRecording();
}
});
}
4.2 波形绘制
使用CustomPainter实现波形可视化:
class WaveformPainter extends CustomPainter {
final List<double> waveformData;
final Color color;
WaveformPainter({
required this.waveformData,
required this.color,
});
void paint(Canvas canvas, Size size) {
if (waveformData.isEmpty) {
// 绘制空状态
final paint = Paint()
..color = color.withValues(alpha: 0.3)
..strokeWidth = 2
..strokeCap = StrokeCap.round;
final centerY = size.height / 2;
canvas.drawLine(
Offset(0, centerY),
Offset(size.width, centerY),
paint,
);
return;
}
final paint = Paint()
..color = color
..strokeWidth = 2
..strokeCap = StrokeCap.round;
final barWidth = size.width / waveformData.length;
final centerY = size.height / 2;
for (int i = 0; i < waveformData.length; i++) {
final x = i * barWidth + barWidth / 2;
final amplitude = waveformData[i] * (size.height / 2 - 4);
canvas.drawLine(
Offset(x, centerY - amplitude),
Offset(x, centerY + amplitude),
paint,
);
}
}
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
4.3 明信片创建
void _createPostcard() {
if (_currentWaveform.isEmpty) {
_showSnackBar('请先录制一段声音');
return;
}
final title = _titleController.text.trim().isEmpty
? '来自远方的声音'
: _titleController.text.trim();
final postcard = Postcard(
id: DateTime.now().millisecondsSinceEpoch.toString(),
title: title,
message: _messageController.text.trim(),
location: _locationController.text.trim().isEmpty
? '某个美好的地方'
: _locationController.text.trim(),
theme: _selectedTheme,
duration: _recordingDuration,
waveformData: List.from(_currentWaveform),
createdAt: DateTime.now(),
);
setState(() {
_postcards.insert(0, postcard);
_previewPostcard = postcard;
});
}
五、UI设计
5.1 色彩系统
| 用途 | 色值 | 说明 |
|---|---|---|
| 主色调 | #FF7F50 | 珊瑚色,温暖活力 |
| 辅助色 | #FFB347 | 金橙色,用于渐变 |
| 录音按钮 | #FF7F50 | 橙色,未录音 |
| 录音按钮 | #FF0000 | 红色,录音中 |
| 波形颜色 | #FF7F50 | 橙色波形 |
5.2 页面结构
┌─────────────────────────────────┐
│ 📧 声音明信片 │ ← 渐变头部
│ 录制声音,制作专属明信片 │
│ ┌─────────┐ ┌─────────┐ │
│ │明信片 3 │ │已寄出 1 │ │ ← 统计卡片
│ └─────────┘ └─────────┘ │
├─────────────────────────────────┤
│ 点击录制环境音 │
│ │
│ ┌─────────────────────────┐ │
│ │ ▎▌▊█▌▎ ▎▌▊█▌▎ ▎▌▊█▌▎ │ │ ← 波形显示
│ └─────────────────────────┘ │
│ │
│ 00:05 │ ← 时长显示
│ │
│ [🔄] [🎤] [✓] │ ← 控制按钮
│ │
├─────────────────────────────────┤
│ 明信片预览 │
│ ┌─────────────────────────┐ │
│ │ 📍海边 🎤 00:05 │ │ ← 明信片预览
│ │ │ │
│ │ 海浪的声音 │ │
│ │ 想念海边的日子... │ │
│ │ │ │
│ │ ▶ ▎▌▊█▌▎ ▎▌▊█▌▎ │ │
│ │ 2024/1/15│
│ └─────────────────────────┘ │
├─────────────────────────────────┤
│ 选择主题 │
│ [🌅日落] [🌊海洋] [🌲森林] │ ← 主题选择
│ [🌃星空] [🏙️城市] [🌸花园] │
├─────────────────────────────────┤
│ ┌─────────────────────────┐ │
│ │ 明信片标题 │ │ ← 输入字段
│ ├─────────────────────────┤ │
│ │ 想说的话... │ │
│ ├─────────────────────────┤ │
│ │ 录制地点 │ │
│ └─────────────────────────┘ │
├─────────────────────────────────┤
│ ▼ 我的明信片 │
│ ┌─────────────────────────┐ │
│ │ 海浪的声音 🎤 00:05 │ │ ← 明信片卡片
│ │ 📍海边 │ │
│ │ ▶ ▎▌▊█▌▎ ▎▌▊█▌▎ │ │
│ │ [寄出] [🗑️] │ │
│ └─────────────────────────┘ │
└─────────────────────────────────┘
5.3 交互设计
| 交互元素 | 触发方式 | 响应行为 |
|---|---|---|
| 录音按钮 | 点击 | 开始/停止录音 |
| 刷新按钮 | 点击 | 重置录音 |
| 确认按钮 | 点击 | 创建明信片 |
| 主题卡片 | 点击 | 切换主题 |
| 寄出按钮 | 点击 | 标记已寄出 |
| 删除按钮 | 点击 | 删除明信片 |
六、动画详解
6.1 录音按钮脉冲动画
录音按钮有呼吸灯效果:
_pulseController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1000),
)..repeat(reverse: true);
// 在UI中使用
Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: color.withValues(alpha: 0.4 + _pulseController.value * 0.2),
blurRadius: 20,
spreadRadius: 5,
),
],
),
)
6.2 波形实时更新
波形每100ms更新一次:
Future.delayed(const Duration(milliseconds: 100), () {
setState(() {
_currentWaveform.add(Random().nextDouble() * 0.8 + 0.2);
if (_currentWaveform.length > 50) {
_currentWaveform.removeAt(0);
}
});
});
6.3 动画时序
七、状态管理
7.1 状态分类
| 状态类型 | 状态名称 | 说明 |
|---|---|---|
| 录音状态 | _isRecording |
是否正在录音 |
| 录音时长 | _recordingDuration |
当前录音时长 |
| 波形数据 | _currentWaveform |
当前波形数据 |
| 主题选择 | _selectedTheme |
当前选中主题 |
| 明信片列表 | _postcards |
所有明信片 |
| 预览明信片 | _previewPostcard |
当前预览的明信片 |
7.2 状态流转
八、波形绘制详解
8.1 波形数据结构
// 波形数据是一个0-1之间的double列表
List<double> waveformData = [0.5, 0.8, 0.3, 0.9, ...];
8.2 绘制算法
8.3 性能优化
| 优化点 | 实现方式 | 效果 |
|---|---|---|
| 数据限制 | 最多保留50个点 | 控制绘制量 |
| 重绘策略 | shouldRepaint返回true | 实时更新 |
| 画笔复用 | Paint对象复用 | 减少对象创建 |
九、性能优化
9.1 渲染优化
| 优化点 | 实现方式 | 效果 |
|---|---|---|
| 波形绘制 | CustomPainter | 高效自定义绘制 |
| 列表优化 | SliverList | 只渲染可见项 |
| 动画优化 | AnimatedBuilder | 只重绘动画部分 |
9.2 内存管理
void dispose() {
_titleController.dispose();
_messageController.dispose();
_locationController.dispose();
_waveformController.dispose();
_pulseController.dispose();
_sendController.dispose();
super.dispose();
}
9.3 性能指标
| 指标 | 目标值 | 实测值 |
|---|---|---|
| 动画帧率 | 60fps | 60fps |
| 内存占用 | < 50MB | 待测试 |
| 启动时间 | < 2s | 待测试 |
十、常见问题
10.1 问题排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 波形不显示 | waveformData为空 | 检查录音逻辑 |
| 主题不切换 | _selectedTheme未更新 | 检查setState调用 |
| 明信片不显示 | _postcards未更新 | 检查创建逻辑 |
| 动画卡顿 | 控制器未释放 | 检查dispose调用 |
10.2 调试技巧
// 打印录音状态
debugPrint('IsRecording: $_isRecording');
debugPrint('Duration: $_recordingDuration');
debugPrint('Waveform length: ${_currentWaveform.length}');
// 检查明信片数量
debugPrint('Postcards: ${_postcards.length}');
十一、运行说明
11.1 环境要求
| 环境 | 版本要求 |
|---|---|
| Flutter SDK | >= 3.0.0 |
| Dart SDK | >= 2.17.0 |
| 鸿蒙OS | API 21+ |
11.2 运行命令
# 查看可用设备
flutter devices
# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 lib/main_sound_postcard.dart
# 运行到Windows
flutter run -d windows -t lib/main_sound_postcard.dart
# 代码分析
flutter analyze lib/main_sound_postcard.dart
十二、扩展建议
12.1 功能扩展
| 功能 | 优先级 | 实现思路 |
|---|---|---|
| 真实录音 | 高 | 集成flutter_sound或record包 |
| 音频播放 | 高 | 集成audioplayers |
| 数据持久化 | 高 | 使用SharedPreferences |
| 分享功能 | 中 | 使用share_plus分享 |
| 云端存储 | 低 | 集成云存储服务 |
12.2 设计扩展
| 方向 | 描述 |
|---|---|
| 更多主题 | 添加更多精美主题配色 |
| 自定义封面 | 允许用户上传图片作为封面 |
| 动画效果 | 寄出时的飞行动画 |
| 音效反馈 | 录音开始/结束的音效 |
十三、总结
声音明信片应用通过创新的"声音+明信片"概念,为用户提供了一种新颖的情感表达方式。应用核心亮点包括:
- 波形可视化:实时显示录音波形
- 精美主题:6种渐变主题配色
- 明信片设计:标题、地点、寄语完整
- 预览功能:实时预览明信片效果
- 寄送标记:标记已寄出状态
应用代码结构清晰,约800行,无第三方依赖(模拟录音),易于理解和维护。后续可扩展真实录音、音频播放、分享功能等,提供更完整的体验。
用声音记录美好,让思念有温度!
更多推荐

所有评论(0)