提交 8631150a authored 作者: songchuancai's avatar songchuancai

优化语音功能

上级 104d31dd
...@@ -8,7 +8,7 @@ if (localPropertiesFile.exists()) { ...@@ -8,7 +8,7 @@ if (localPropertiesFile.exists()) {
def flutterRoot = localProperties.getProperty('flutter.sdk') def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) { if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") throw new FileNotFoundException ("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
} }
def flutterVersionCode = localProperties.getProperty('flutter.versionCode') def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<queries> <queries>
<intent> <intent>
<action android:name="android.speech.RecognitionService" /> <action android:name="android.speech.RecognitionService" />
......
...@@ -92,6 +92,8 @@ class _HomePageState extends State<HomePage> { ...@@ -92,6 +92,8 @@ class _HomePageState extends State<HomePage> {
int _currentIndex = 0; int _currentIndex = 0;
bool _hasSpeechPermission = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
...@@ -162,15 +164,47 @@ class _HomePageState extends State<HomePage> { ...@@ -162,15 +164,47 @@ class _HomePageState extends State<HomePage> {
} }
Future<void> initSpeechToText() async { Future<void> initSpeechToText() async {
await speechToText.initialize(); try {
_hasSpeechPermission = await speechToText.initialize(
onStatus: (status) {
print('语音状态: $status');
if (status == 'notListening') {
setState(() => _isListeningPressed = false);
}
},
onError: (error) {
print('语音错误: $error');
setState(() => _isListeningPressed = false);
_showErrorDialog('语音初始化失败,请检查麦克风权限');
},
);
} catch (e) {
print('语音初始化错误: $e');
_hasSpeechPermission = false;
}
setState(() {}); setState(() {});
} }
Future<void> startListening() async { Future<void> startListening() async {
await speechToText.listen(onResult: onSpeechResult); if (!_hasSpeechPermission) {
_showErrorDialog('请先授予麦克风权限');
setState(() => _isListeningPressed = false);
return;
}
setState(() {}); try {
await speechToText.listen(
onResult: onSpeechResult,
localeId: 'zh_CN',
listenMode: ListenMode.confirmation,
cancelOnError: true,
partialResults: true,
);
} catch (e) {
print('开始录音失败: $e');
setState(() => _isListeningPressed = false);
_showErrorDialog('启动语音识别失败,请重试');
}
} }
Future<void> stopListening() async { Future<void> stopListening() async {
...@@ -372,38 +406,32 @@ class _HomePageState extends State<HomePage> { ...@@ -372,38 +406,32 @@ class _HomePageState extends State<HomePage> {
Expanded( Expanded(
child: _isVoiceMode child: _isVoiceMode
? GestureDetector( ? GestureDetector(
onLongPressStart: (_) async { onTap: () async {
if (!_isListeningPressed) {
// 开始录音
setState(() { setState(() {
_isListeningPressed = true; _isListeningPressed = true;
_currentVoiceText = ''; _currentVoiceText = '';
}); });
await startListening(); await startListening();
}, } else {
onLongPressEnd: (_) async { // 停止录音
setState(() => _isListeningPressed = false); setState(() => _isListeningPressed = false);
await stopListening(); await stopListening();
final finalVoiceText = _currentVoiceText; // 确保有语音文本才处理
if (_currentVoiceText.isNotEmpty) {
if (finalVoiceText.isNotEmpty) {
setState(() { setState(() {
messages.add(ChatMessage( messages.add(ChatMessage(
text: finalVoiceText, text: _currentVoiceText,
isUserMessage: true, isUserMessage: true,
)); ));
_isLoading = true; _isLoading = true;
}); });
await _processAIResponse(_currentVoiceText);
await _processAIResponse(finalVoiceText); }
setState(() => _currentVoiceText = '');
} }
setState(() {
_currentVoiceText = '';
});
}, },
child: Container( child: Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
...@@ -414,12 +442,20 @@ class _HomePageState extends State<HomePage> { ...@@ -414,12 +442,20 @@ class _HomePageState extends State<HomePage> {
color: Colors.grey[100], color: Colors.grey[100],
borderRadius: BorderRadius.circular(25), borderRadius: BorderRadius.circular(25),
), ),
child: Text( child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
_isListeningPressed ? Icons.stop : Icons.mic,
color: _isListeningPressed
? Pallete.firstSuggestionBoxColor
: Colors.grey[600],
),
const SizedBox(width: 8),
Text(
_isListeningPressed _isListeningPressed
? (_currentVoiceText.isEmpty ? (_currentVoiceText.isEmpty ? '点击停止' : _currentVoiceText)
? '正在聆听...' : '点击说话',
: _currentVoiceText)
: '按住说话',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
color: _isListeningPressed color: _isListeningPressed
...@@ -427,6 +463,8 @@ class _HomePageState extends State<HomePage> { ...@@ -427,6 +463,8 @@ class _HomePageState extends State<HomePage> {
: Colors.grey[600], : Colors.grey[600],
), ),
), ),
],
),
), ),
) )
: TextField( : TextField(
...@@ -703,7 +741,7 @@ class _HomePageState extends State<HomePage> { ...@@ -703,7 +741,7 @@ class _HomePageState extends State<HomePage> {
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'退出登录', '出登录',
style: TextStyle( style: TextStyle(
color: Colors.red[700], color: Colors.red[700],
fontSize: 16, fontSize: 16,
...@@ -724,6 +762,22 @@ class _HomePageState extends State<HomePage> { ...@@ -724,6 +762,22 @@ class _HomePageState extends State<HomePage> {
); );
} }
void _showErrorDialog(String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('提示'),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('确定'),
),
],
),
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
...@@ -850,3 +904,4 @@ class _HomePageState extends State<HomePage> { ...@@ -850,3 +904,4 @@ class _HomePageState extends State<HomePage> {
); );
} }
} }
\ No newline at end of file
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论