声音明信片应用


欢迎加入开源鸿蒙跨平台社区:
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 动画时序

开始录音 0ms 按钮变红 0ms 开始波形动画 录音中 每100ms 添加波形数据 每100ms 更新时长显示 停止录音 点击停止 保存波形数据 点击确认 创建明信片 录音流程时序图

七、状态管理

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 设计扩展

方向 描述
更多主题 添加更多精美主题配色
自定义封面 允许用户上传图片作为封面
动画效果 寄出时的飞行动画
音效反馈 录音开始/结束的音效

十三、总结

声音明信片应用通过创新的"声音+明信片"概念,为用户提供了一种新颖的情感表达方式。应用核心亮点包括:

  1. 波形可视化:实时显示录音波形
  2. 精美主题:6种渐变主题配色
  3. 明信片设计:标题、地点、寄语完整
  4. 预览功能:实时预览明信片效果
  5. 寄送标记:标记已寄出状态

应用代码结构清晰,约800行,无第三方依赖(模拟录音),易于理解和维护。后续可扩展真实录音、音频播放、分享功能等,提供更完整的体验。

用声音记录美好,让思念有温度!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐