flutter_voice_commands 0.0.1
flutter_voice_commands: ^0.0.1 copied to clipboard
Voice command recognition and processing for Flutter applications
import 'package:flutter/material.dart';
import 'package:flutter_voice_commands/flutter_voice_commands.dart';
void main() {
runApp(const VoiceCommandsExampleApp());
}
class VoiceCommandsExampleApp extends StatelessWidget {
const VoiceCommandsExampleApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
title: 'Flutter Voice Commands Example',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
home: const VoiceCommandsExample(),
);
}
class VoiceCommandsExample extends StatefulWidget {
const VoiceCommandsExample({super.key});
@override
State<VoiceCommandsExample> createState() => _VoiceCommandsExampleState();
}
class _VoiceCommandsExampleState extends State<VoiceCommandsExample> {
final FlutterVoiceCommands _voiceCommands = FlutterVoiceCommands();
bool _isListening = false;
bool _isInitialized = false;
String _recognizedText = '';
String _lastCommand = '';
final List<String> _commandHistory = [];
@override
void initState() {
super.initState();
_initializeVoiceCommands();
_setupCommands();
}
Future<void> _initializeVoiceCommands() async {
try {
await _voiceCommands.initialize();
setState(() => _isInitialized = true);
} catch (e) {
_showError('Initialization failed: $e');
}
}
void _setupCommands() {
final commands = [
VoiceCommand(
pattern: 'open (\\w+)',
action: (matches) => _executeCommand('open', matches[1]),
description: 'Open an application or file',
priority: 3,
),
VoiceCommand(
pattern: 'close (\\w+)',
action: (matches) => _executeCommand('close', matches[1]),
description: 'Close an application or file',
priority: 3,
),
VoiceCommand(
pattern: 'search for (\\w+)',
action: (matches) => _executeCommand('search', matches[1]),
description: 'Search for something',
priority: 2,
),
VoiceCommand(
pattern: 'play (\\w+)',
action: (matches) => _executeCommand('play', matches[1]),
description: 'Play media or start something',
priority: 2,
),
VoiceCommand(
pattern: 'stop',
action: (matches) => _executeCommand('stop', ''),
description: 'Stop current action',
priority: 1,
),
VoiceCommand(
pattern: 'help',
action: (matches) => _showHelp(),
description: 'Show available commands',
priority: 1,
),
];
_voiceCommands.registerCommands(commands);
}
void _executeCommand(String command, String target) {
final commandText = target.isNotEmpty ? '$command $target' : command;
setState(() {
_lastCommand = commandText;
_commandHistory.insert(0, commandText);
if (_commandHistory.length > 10) {
_commandHistory.removeLast();
}
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Executed: $commandText'),
duration: const Duration(seconds: 2),
),
);
}
void _showHelp() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Available Voice Commands'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: _voiceCommands.commands.map((cmd) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Text(
'• "${cmd.pattern}" - ${cmd.description ?? "No description"}',
style: const TextStyle(fontSize: 14),
),
);
}).toList(),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'),
),
],
),
);
}
Future<void> _startListening() async {
if (!_isInitialized) {
_showError('Voice recognition not initialized');
return;
}
if (await _voiceCommands.hasPermission()) {
setState(() => _isListening = true);
try {
await _voiceCommands.listenWithCommands(
onCommand: (command, matches) {
command.execute(matches);
},
onUnknown: (text) {
setState(() => _recognizedText = text);
},
);
} catch (e) {
_showError('Error starting voice recognition: $e');
setState(() => _isListening = false);
}
} else {
final granted = await _voiceCommands.requestPermission();
if (granted) {
_startListening();
} else {
_showError('Microphone permission denied');
}
}
}
Future<void> _stopListening() async {
await _voiceCommands.stop();
setState(() => _isListening = false);
}
void _showError(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.red,
duration: const Duration(seconds: 3),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Voice Commands Example'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
actions: [
IconButton(
icon: const Icon(Icons.help),
onPressed: _showHelp,
tooltip: 'Show available commands',
),
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Status Card
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Status',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 8),
Row(
children: [
Icon(
_isInitialized ? Icons.check_circle : Icons.error,
color: _isInitialized ? Colors.green : Colors.red,
),
const SizedBox(width: 8),
Text('Initialized: ${_isInitialized ? "Yes" : "No"}'),
],
),
const SizedBox(height: 4),
Row(
children: [
Icon(
_isListening ? Icons.mic : Icons.mic_off,
color: _isListening ? Colors.green : Colors.grey,
),
const SizedBox(width: 8),
Text('Listening: ${_isListening ? "Yes" : "No"}'),
],
),
],
),
),
),
const SizedBox(height: 16),
// Control Buttons
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: _isListening ? _stopListening : _startListening,
icon: Icon(_isListening ? Icons.stop : Icons.mic),
label: Text(
_isListening ? 'Stop Listening' : 'Start Listening'),
style: ElevatedButton.styleFrom(
backgroundColor: _isListening ? Colors.red : Colors.blue,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
),
),
),
],
),
const SizedBox(height: 16),
// Recognized Text Card
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Recognized Text',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 8),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(8),
color: Colors.grey.shade50,
),
child: Text(
_recognizedText.isEmpty
? 'No text recognized yet'
: _recognizedText,
style: const TextStyle(fontSize: 16),
),
),
],
),
),
),
const SizedBox(height: 16),
// Last Command Card
if (_lastCommand.isNotEmpty) ...[
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Last Command',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 8),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
border: Border.all(color: Colors.green.shade300),
borderRadius: BorderRadius.circular(8),
color: Colors.green.shade50,
),
child: Text(
_lastCommand,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
),
],
),
),
),
const SizedBox(height: 16),
],
// Command History Card
if (_commandHistory.isNotEmpty) ...[
Expanded(
child: Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Command History',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 8),
Expanded(
child: ListView.builder(
itemCount: _commandHistory.length,
itemBuilder: (context, index) {
return ListTile(
leading: const Icon(Icons.history),
title: Text(_commandHistory[index]),
dense: true,
);
},
),
),
],
),
),
),
),
],
],
),
),
);
}
@override
void dispose() {
_voiceCommands.dispose();
super.dispose();
}
}