voice_audio_visualizer 1.0.1
voice_audio_visualizer: ^1.0.1 copied to clipboard
A comprehensive Flutter audio package with recording, playback, editing, and waveform visualization. Features customizable widgets, voice message bubbles, and cross-platform support.
import 'package:flutter/material.dart';
import 'package:voice_audio_visualizer/voice_audio_visualizer.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Audio Visualizer Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Audio Visualizer'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildSection(
context,
'Recording',
'Record audio with live waveform',
Icons.mic,
() => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const RecorderDemo()),
),
),
_buildSection(
context,
'Playback',
'Play audio with waveform visualization',
Icons.play_circle,
() => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const PlayerDemo()),
),
),
_buildSection(
context,
'Voice Messages',
'Chat-style voice message bubbles',
Icons.chat_bubble,
() => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const VoiceBubbleDemo()),
),
),
_buildSection(
context,
'Editor',
'Edit audio with trim and delete',
Icons.content_cut,
() => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const EditorDemo()),
),
),
_buildSection(
context,
'Waveform Styles',
'Different waveform visualizations',
Icons.waves,
() => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const WaveformStylesDemo()),
),
),
],
),
);
}
Widget _buildSection(
BuildContext context,
String title,
String subtitle,
IconData icon,
VoidCallback onTap,
) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: ListTile(
leading: CircleAvatar(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
child: Icon(icon, color: Theme.of(context).colorScheme.primary),
),
title: Text(title),
subtitle: Text(subtitle),
trailing: const Icon(Icons.chevron_right),
onTap: onTap,
),
);
}
}
// Recording Demo
class RecorderDemo extends StatefulWidget {
const RecorderDemo({super.key});
@override
State<RecorderDemo> createState() => _RecorderDemoState();
}
class _RecorderDemoState extends State<RecorderDemo> {
AudioFile? _recordedFile;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Recording Demo')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
AudioRecorderWidget(
waveformStyle: const WaveformStyle(
waveColor: Colors.blue,
type: WaveformType.bars,
),
onRecordingComplete: (file) {
setState(() => _recordedFile = file);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Recorded: ${file.formattedDuration}')),
);
},
),
if (_recordedFile != null) ...[
const SizedBox(height: 24),
const Text('Recorded Audio:', style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
AudioPlayerWidget(
filePath: _recordedFile!.path,
waveformData: _recordedFile!.waveformData,
),
],
],
),
),
);
}
}
// Player Demo
class PlayerDemo extends StatelessWidget {
const PlayerDemo({super.key});
@override
Widget build(BuildContext context) {
// Using placeholder waveform for demo
final sampleWaveform = WaveformExtractor.generatePlaceholder(60);
return Scaffold(
appBar: AppBar(title: const Text('Player Demo')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
const Text(
'Audio Player with Waveform',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
AudioPlayerWidget(
// Replace with actual audio URL
url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
waveformData: sampleWaveform.samples,
waveformStyle: const WaveformStyle(
waveColor: Colors.grey,
playedColor: Colors.blue,
type: WaveformType.bars,
),
showSpeedControl: true,
),
],
),
),
);
}
}
// Voice Bubble Demo
class VoiceBubbleDemo extends StatelessWidget {
const VoiceBubbleDemo({super.key});
@override
Widget build(BuildContext context) {
final sampleWaveform = WaveformExtractor.generatePlaceholder(40);
return Scaffold(
appBar: AppBar(title: const Text('Voice Messages')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
const Text('WhatsApp Style', style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
VoiceMessageBubble(
url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
waveformData: sampleWaveform.samples,
style: VoiceBubbleStyle.whatsApp,
isSent: true,
timestamp: DateTime.now(),
),
const SizedBox(height: 24),
const Text('Telegram Style', style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
VoiceMessageBubble(
url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
waveformData: sampleWaveform.samples,
style: VoiceBubbleStyle.telegram,
isSent: false,
timestamp: DateTime.now().subtract(const Duration(hours: 2)),
),
const SizedBox(height: 24),
const Text('Custom Style', style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
VoiceMessageBubble(
url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
waveformData: sampleWaveform.samples,
style: const VoiceBubbleStyle(
bubbleColor: Color(0xFFE8EAF6),
iconColor: Color(0xFF3F51B5),
borderRadius: 20,
),
waveformStyle: const WaveformStyle(
waveColor: Color(0xFFBDBDBD),
playedColor: Color(0xFF3F51B5),
type: WaveformType.rounded,
),
isSent: true,
),
],
),
);
}
}
// Editor Demo
class EditorDemo extends StatelessWidget {
const EditorDemo({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Audio Editor')),
body: const Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Audio Editor',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
'Drag on the waveform to select a region, then use the controls to trim or delete.',
style: TextStyle(color: Colors.grey),
),
SizedBox(height: 16),
// AudioEditorWidget requires a local file path
// For demo, you would record first then edit
Center(
child: Text(
'Record an audio first, then edit it here.',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
],
),
),
);
}
}
// Waveform Styles Demo
class WaveformStylesDemo extends StatelessWidget {
const WaveformStylesDemo({super.key});
@override
Widget build(BuildContext context) {
final samples = WaveformExtractor.generatePlaceholder(80).samples;
return Scaffold(
appBar: AppBar(title: const Text('Waveform Styles')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildWaveformDemo('Bars (Default)', samples, WaveformType.bars),
_buildWaveformDemo('Line', samples, WaveformType.line),
_buildWaveformDemo('Rounded', samples, WaveformType.rounded),
_buildWaveformDemo('Mirrored', samples, WaveformType.mirrored),
_buildWaveformDemo('SoundCloud', samples, WaveformType.soundCloud),
],
),
);
}
Widget _buildWaveformDemo(String title, List<double> samples, WaveformType type) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Container(
height: 60,
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
child: AudioWaveform(
samples: samples,
progress: 0.4,
style: WaveformStyle(
waveColor: Colors.grey,
playedColor: Colors.blue,
type: type,
),
height: 60,
),
),
const SizedBox(height: 24),
],
);
}
}