audio_toolkit 0.0.7
audio_toolkit: ^0.0.7 copied to clipboard
A macOS-only Flutter plugin to record system/microphone audio and transcribe voice to text with real-time speech detection.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:audio_toolkit_example/cubit/audio_toolkit_cubit.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: BlocProvider(
create: (_) => AudioToolkitCubit()..init(),
child: const AudioToolkitScreen(),
),
);
}
}
class AudioToolkitScreen extends StatelessWidget {
const AudioToolkitScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: BlocConsumer<AudioToolkitCubit, AudioToolkitState>(
listenWhen: (prev, curr) =>
prev is AudioToolkitInitial &&
curr is AudioToolkitInitial &&
prev.path != curr.path,
listener: (context, state) {
if (state is AudioToolkitInitial && state.path.isNotEmpty) {
context.read<AudioToolkitCubit>().transcribeAudio(state.path);
}
},
builder: (context, state) {
if (state is! AudioToolkitInitial) {
return const Center(child: CircularProgressIndicator());
}
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (state.text.isNotEmpty)
Expanded(child: _buildTranscriptionBox(state.text)),
const SizedBox(height: 30),
_buildRecordingControls(context, state),
const SizedBox(height: 16),
_buildMicSection(),
const SizedBox(height: 16),
_buildRecordingButton(context, state),
const SizedBox(height: 30),
],
),
);
},
),
);
}
Widget _buildTranscriptionBox(List<String> text) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.pinkAccent.shade100,
borderRadius: BorderRadius.circular(8),
),
child: ListView.builder(
reverse: true,
itemCount: text.length,
itemBuilder: (_, index) => Text(
text[index],
style: const TextStyle(color: Colors.white),
),
),
);
}
Widget _buildRecordingControls(
BuildContext context, AudioToolkitInitial state) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () {
final cubit = context.read<AudioToolkitCubit>();
state.isSystemRecord
? cubit.turnOffSystemRecording()
: cubit.turnOnSystemRecording();
},
child: Icon(
state.isSystemRecord
? Icons.desktop_mac
: Icons.desktop_access_disabled,
size: 40,
),
),
const SizedBox(width: 16),
_buildDbMeter(state.db),
],
);
}
Widget _buildMicSection() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.mic, size: 40),
SizedBox(width: 16),
// Hiếu định làm mic riêng ở đây sau, placeholder db -160
_buildDbMeter(-160),
],
);
}
Widget _buildRecordingButton(
BuildContext context, AudioToolkitInitial state) {
final isRecording = state.isRecording;
final cubit = context.read<AudioToolkitCubit>();
return ElevatedButton.icon(
icon: Icon(isRecording ? Icons.stop : Icons.fiber_manual_record),
label: Text(isRecording ? 'Dừng ghi' : 'Bắt đầu ghi'),
style: ElevatedButton.styleFrom(
backgroundColor: isRecording ? Colors.red : Colors.green,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
onPressed: () =>
isRecording ? cubit.stopRecording() : cubit.startRecord(),
);
}
static Widget _buildDbMeter(double db) {
final normalized = ((db + 60).clamp(0, 60)) / 60;
Color getColor() {
if (db > -20) return Colors.red;
if (db > -40) return Colors.orange;
return Colors.green;
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Âm lượng hiện tại: ${(normalized * 100).toStringAsFixed(1)}%'),
const SizedBox(height: 6),
Container(
width: 240,
height: 14,
decoration: BoxDecoration(
border: Border.all(color: Colors.black26),
borderRadius: BorderRadius.circular(4),
),
child: Align(
alignment: Alignment.centerLeft,
child: AnimatedContainer(
duration: const Duration(milliseconds: 100),
width: 240 * normalized,
height: 14,
decoration: BoxDecoration(
color: getColor(),
borderRadius: BorderRadius.circular(4),
),
),
),
),
],
);
}
}