flutter_audio_recorder_kit 0.1.4
flutter_audio_recorder_kit: ^0.1.4 copied to clipboard
Drop-in Flutter audio recorder with live waveform, playback, WAV trimming, theming, Android foreground-service, and auto-pause on call.
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_audio_recorder_kit/flutter_audio_recorder_kit.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const ExampleApp());
}
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Audio Recorder Kit',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF8B5CF6),
brightness: Brightness.dark,
),
scaffoldBackgroundColor: const Color(0xFF0B1020),
),
home: const DemoListScreen(),
);
}
}
/// Lists one demo per feature combination. In a real app you would simply
/// embed [AudioRecorderWidget] with the features your screen needs.
class DemoListScreen extends StatelessWidget {
const DemoListScreen({super.key});
@override
Widget build(BuildContext context) {
final demos = <(String, String, WidgetBuilder)>[
(
'Full kit',
'Recording, playback, trimming, and upload — everything on.',
(_) => const _DemoScaffold(
title: 'Full kit',
child: AudioRecorderWidget(
features: RecorderFeatures.all,
showAppBarRow: false,
),
),
),
(
'Recording only',
'Just the recorder — no result panel after stopping.',
(_) => _DemoScaffold(
title: 'Recording only',
child: AudioRecorderWidget(
features: RecorderFeatures.recordOnly,
showAppBarRow: false,
onRecordingComplete: (path) =>
debugPrint('Recording saved: $path'),
),
),
),
(
'Record + trim, no playback',
'Record, then trim the WAV without playback controls.',
(_) => const _DemoScaffold(
title: 'Record + trim',
child: AudioRecorderWidget(
features: RecorderFeatures.recordAndTrim,
showAppBarRow: false,
),
),
),
(
'Playback only (upload)',
'No recording — upload an audio file and play it.',
(_) => const _DemoScaffold(
title: 'Playback only',
child: AudioRecorderWidget(
features: RecorderFeatures.playbackOnly,
showAppBarRow: false,
),
),
),
(
'Standalone playback panel',
'AudioPlaybackPanel with a file you pick — no recorder UI at all.',
(_) => const StandalonePanelDemo(),
),
(
'Custom theme',
'Theme derived from your app ColorScheme.',
(_) => const ThemedDemo(),
),
];
return Scaffold(
appBar: AppBar(title: const Text('Audio Recorder Kit demos')),
body: ListView.separated(
padding: const EdgeInsets.all(16),
itemCount: demos.length,
separatorBuilder: (context, index) => const SizedBox(height: 8),
itemBuilder: (context, index) {
final (title, subtitle, builder) = demos[index];
return Card(
child: ListTile(
title: Text(title),
subtitle: Text(subtitle),
trailing: const Icon(Icons.chevron_right),
onTap: () => Navigator.of(context)
.push(MaterialPageRoute<void>(builder: builder)),
),
);
},
),
);
}
}
class _DemoScaffold extends StatelessWidget {
const _DemoScaffold({required this.title, required this.child});
final String title;
final Widget child;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: child,
);
}
}
/// Demonstrates [AudioPlaybackPanel] on its own: pick any audio file and the
/// panel plays (and, for WAV, trims) it.
class StandalonePanelDemo extends StatefulWidget {
const StandalonePanelDemo({super.key});
@override
State<StandalonePanelDemo> createState() => _StandalonePanelDemoState();
}
class _StandalonePanelDemoState extends State<StandalonePanelDemo> {
String? _filePath;
Future<void> _pickFile() async {
final result = await FilePicker.pickFiles(
type: FileType.custom,
allowedExtensions: ['mp3', 'wav', 'm4a', 'aac', 'flac', 'ogg'],
);
final path = result?.files.single.path;
if (path != null && mounted) {
setState(() => _filePath = path);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Standalone playback panel')),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
FilledButton.icon(
onPressed: _pickFile,
icon: const Icon(Icons.upload_file_rounded),
label: const Text('Pick an audio file'),
),
const SizedBox(height: 20),
if (_filePath != null)
AudioPlaybackPanel(
key: ValueKey(_filePath),
filePath: _filePath!,
onTrimComplete: (path) => debugPrint('Trimmed: $path'),
)
else
const Center(child: Text('No file selected yet.')),
],
),
),
);
}
}
/// Demonstrates theming: the recorder UI derives its colors from the app's
/// [ColorScheme] via [AudioRecorderTheme.fromColorScheme].
class ThemedDemo extends StatelessWidget {
const ThemedDemo({super.key});
@override
Widget build(BuildContext context) {
final scheme = ColorScheme.fromSeed(
seedColor: Colors.teal,
brightness: Brightness.dark,
);
return Scaffold(
appBar: AppBar(title: const Text('Custom theme')),
body: AudioRecorderWidget(
theme: AudioRecorderTheme.fromColorScheme(scheme),
features: RecorderFeatures.all,
showAppBarRow: false,
config: const RecordingConfig(format: RecordingFormat.aacM4a),
),
);
}
}