Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
F
flutter-chat
Project
Project
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
Graph
比较
统计图
议题
0
议题
0
列表
看板
标记
Milestones
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
songchuancai
flutter-chat
Commits
c2c63647
提交
c2c63647
authored
11月 12, 2024
作者:
songchuancai
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
增加会话名称自动生成
上级
0f950dc7
隐藏空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
982 行增加
和
910 行删除
+982
-910
home_page.dart
lib/home_page.dart
+939
-908
chat_page.dart
lib/pages/chat_page.dart
+10
-0
chat_service.dart
lib/services/chat_service.dart
+31
-0
GeneratedPluginRegistrant.swift
macos/Flutter/GeneratedPluginRegistrant.swift
+2
-2
没有找到文件。
lib/home_page.dart
浏览文件 @
c2c63647
import
'package:allen/pallete.dart'
;
import
'package:animate_do/animate_do.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_tts/flutter_tts.dart'
;
import
'package:speech_to_text/speech_recognition_result.dart'
;
import
'package:speech_to_text/speech_to_text.dart'
;
import
'package:flutter/foundation.dart'
show
kIsWeb
;
import
'package:uuid/uuid.dart'
;
import
'models/conversation.dart'
;
import
'services/storage_service.dart'
;
import
'services/chat_service.dart'
;
import
'models/chat_message.dart'
;
import
'models/user.dart'
;
import
'package:shared_preferences/shared_preferences.dart'
;
import
'pages/apps_page.dart'
;
import
'pages/chat_bubble.dart'
;
class
HomePage
extends
StatefulWidget
{
final
String
?
customTitle
;
final
String
?
customDescription
;
final
String
?
customImageUrl
;
final
bool
hideNavigation
;
const
HomePage
({
super
.
key
,
this
.
customTitle
,
this
.
customDescription
,
this
.
customImageUrl
,
this
.
hideNavigation
=
false
,
});
@override
State
<
HomePage
>
createState
()
=>
_HomePageState
();
}
class
_HomePageState
extends
State
<
HomePage
>
{
final
speechToText
=
SpeechToText
();
final
flutterTts
=
FlutterTts
();
String
lastWords
=
''
;
final
OpenAIService
openAIService
=
OpenAIService
();
String
?
generatedContent
;
String
?
generatedImageUrl
;
int
start
=
200
;
int
delay
=
200
;
final
TextEditingController
_messageController
=
TextEditingController
();
String
currentStreamedContent
=
''
;
List
<
ChatMessage
>
messages
=
[];
bool
_isLoading
=
false
;
bool
_isVoiceMode
=
true
;
bool
_isListeningPressed
=
false
;
String
_currentVoiceText
=
''
;
late
StorageService
_storageService
;
late
SharedPreferences
_prefs
;
late
Conversation
_currentConversation
;
List
<
Conversation
>
_conversations
=
[];
int
_currentIndex
=
0
;
bool
_hasSpeechPermission
=
false
;
@override
void
initState
()
{
super
.
initState
();
_initializeStorage
();
initSpeechToText
();
_initTts
();
}
Future
<
void
>
_initializeStorage
()
async
{
_prefs
=
await
SharedPreferences
.
getInstance
();
_storageService
=
StorageService
(
_prefs
);
_conversations
=
await
_storageService
.
getConversations
();
// 每次进入应用创建一个新的会话
_createNewConversation
();
// if (_conversations.isEmpty) {
// _createNewConversation();
// } else {
// _currentConversation = _conversations.first;
// setState(() {
// messages = _currentConversation.messages;
// });
// }
}
Future
<
void
>
_createNewConversation
()
async
{
final
newConversation
=
Conversation
(
id:
const
Uuid
().
v4
(),
title:
'新会话
${_conversations.length + 1}
'
,
createdAt:
DateTime
.
now
(),
messages:
[],
);
await
_storageService
.
addConversation
(
newConversation
);
setState
(()
{
_conversations
.
insert
(
0
,
newConversation
);
_currentConversation
=
newConversation
;
messages
=
[];
});
}
Future
<
void
>
_updateCurrentConversation
()
async
{
_currentConversation
=
Conversation
(
id:
_currentConversation
.
id
,
title:
_currentConversation
.
title
,
createdAt:
_currentConversation
.
createdAt
,
messages:
messages
,
);
await
_storageService
.
updateConversation
(
_currentConversation
);
}
Future
<
void
>
_initTts
()
async
{
if
(!
kIsWeb
)
{
await
flutterTts
.
setSharedInstance
(
true
);
}
setState
(()
{});
}
Future
<
void
>
initSpeechToText
()
async
{
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
(()
{});
}
Future
<
void
>
startListening
()
async
{
if
(!
_hasSpeechPermission
)
{
_showErrorDialog
(
'请先授予麦克风权限'
);
setState
(()
=>
_isListeningPressed
=
false
);
return
;
}
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
{
await
speechToText
.
stop
();
setState
(()
{});
}
Future
<
void
>
onSpeechResult
(
SpeechRecognitionResult
result
)
async
{
setState
(()
{
lastWords
=
result
.
recognizedWords
;
_currentVoiceText
=
result
.
recognizedWords
;
});
}
Future
<
void
>
systemSpeak
(
String
content
)
async
{
try
{
if
(
kIsWeb
)
{
// 设置语速
await
flutterTts
.
setSpeechRate
(
1
);
// 音调
await
flutterTts
.
setPitch
(
0.8
);
await
flutterTts
.
speak
(
content
);
}
else
{
await
flutterTts
.
setSharedInstance
(
true
);
await
flutterTts
.
speak
(
content
);
}
}
catch
(
e
)
{
print
(
'TTS Error:
$e
'
);
}
}
Future
<
void
>
_sendMessage
()
async
{
if
(!
mounted
)
return
;
// 添加这行
if
(
_messageController
.
text
.
isEmpty
)
return
;
String
userMessage
=
_messageController
.
text
;
_messageController
.
clear
();
setState
(()
{
messages
.
add
(
ChatMessage
(
text:
userMessage
,
isUserMessage:
true
,
));
currentStreamedContent
=
''
;
_isLoading
=
true
;
});
try
{
String
fullResponse
=
''
;
bool
isFirstChunk
=
true
;
// 创建一个缓冲区来存储收到的文本
StringBuffer
buffer
=
StringBuffer
();
await
for
(
final
chunk
in
openAIService
.
chatGPTAPI
(
userMessage
))
{
buffer
.
write
(
chunk
);
// 逐字显示文本
for
(
int
i
=
fullResponse
.
length
;
i
<
buffer
.
length
;
i
++)
{
setState
(()
{
fullResponse
+=
buffer
.
toString
()[
i
];
if
(
isFirstChunk
&&
i
==
0
)
{
messages
.
add
(
ChatMessage
(
text:
fullResponse
,
isUserMessage:
false
,
));
isFirstChunk
=
false
;
}
else
{
messages
.
last
=
ChatMessage
(
text:
fullResponse
,
isUserMessage:
false
,
);
}
});
// 添加短暂延迟以创建打字效果
await
Future
.
delayed
(
const
Duration
(
milliseconds:
50
));
}
}
//await systemSpeak(fullResponse);
await
_updateCurrentConversation
();
}
catch
(
e
)
{
setState
(()
{
messages
.
add
(
ChatMessage
(
text:
'抱歉,出现了一些错误:
$e
'
,
isUserMessage:
false
,
));
});
}
finally
{
setState
(()
{
_isLoading
=
false
;
});
}
}
Future
<
void
>
_processAIResponse
(
String
userInput
)
async
{
try
{
String
fullResponse
=
''
;
bool
isFirstChunk
=
true
;
// 创建一个缓冲区来储收到的文本
StringBuffer
buffer
=
StringBuffer
();
await
for
(
final
chunk
in
openAIService
.
chatGPTAPI
(
userInput
))
{
buffer
.
write
(
chunk
);
// 逐字显示文本
for
(
int
i
=
fullResponse
.
length
;
i
<
buffer
.
length
;
i
++)
{
setState
(()
{
fullResponse
+=
buffer
.
toString
()[
i
];
if
(
isFirstChunk
&&
i
==
0
)
{
messages
.
add
(
ChatMessage
(
text:
fullResponse
,
isUserMessage:
false
,
));
isFirstChunk
=
false
;
}
else
{
messages
.
last
=
ChatMessage
(
text:
fullResponse
,
isUserMessage:
false
,
);
}
});
// 添加短暂延迟以创建打字效果
await
Future
.
delayed
(
const
Duration
(
milliseconds:
50
));
}
}
// await systemSpeak(fullResponse);
await
_updateCurrentConversation
();
}
catch
(
e
)
{
setState
(()
{
messages
.
add
(
ChatMessage
(
text:
'抱歉,出现了一些错误:
$e
'
,
isUserMessage:
false
,
));
});
}
finally
{
setState
(()
{
_isLoading
=
false
;
});
}
}
@override
void
dispose
()
{
super
.
dispose
();
speechToText
.
stop
();
flutterTts
.
stop
();
_messageController
.
dispose
();
}
Widget
_buildBottomInput
()
{
return
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
8
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
boxShadow:
[
BoxShadow
(
color:
Colors
.
grey
.
withOpacity
(
0.1
),
spreadRadius:
1
,
blurRadius:
3
,
offset:
const
Offset
(
0
,
-
1
),
),
],
),
child:
Row
(
children:
[
Expanded
(
child:
_isVoiceMode
?
GestureDetector
(
onTap:
()
async
{
if
(!
_isListeningPressed
)
{
// 开始录音
setState
(()
{
_isListeningPressed
=
true
;
_currentVoiceText
=
''
;
});
await
startListening
();
}
else
{
// 停止录音
setState
(()
=>
_isListeningPressed
=
false
);
await
stopListening
();
// 确保有语音文本才处理
if
(
_currentVoiceText
.
isNotEmpty
)
{
setState
(()
{
messages
.
add
(
ChatMessage
(
text:
_currentVoiceText
,
isUserMessage:
true
,
));
_isLoading
=
true
;
});
await
_processAIResponse
(
_currentVoiceText
);
}
setState
(()
=>
_currentVoiceText
=
''
);
}
},
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
20
,
vertical:
10
,
),
decoration:
BoxDecoration
(
color:
Colors
.
grey
[
100
],
borderRadius:
BorderRadius
.
circular
(
25
),
),
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
?
(
_currentVoiceText
.
isEmpty
?
'点击停止'
:
_currentVoiceText
)
:
'点击说话'
,
textAlign:
TextAlign
.
center
,
style:
TextStyle
(
color:
_isListeningPressed
?
Pallete
.
firstSuggestionBoxColor
:
Colors
.
grey
[
600
],
),
),
],
),
),
)
:
TextField
(
controller:
_messageController
,
decoration:
InputDecoration
(
hintText:
'输入消息...'
,
border:
OutlineInputBorder
(
borderRadius:
BorderRadius
.
circular
(
25
),
borderSide:
BorderSide
.
none
,
),
filled:
true
,
fillColor:
Colors
.
grey
[
100
],
contentPadding:
const
EdgeInsets
.
symmetric
(
horizontal:
20
,
vertical:
10
,
),
),
),
),
const
SizedBox
(
width:
8
),
IconButton
(
icon:
Icon
(
_isVoiceMode
?
Icons
.
keyboard
:
Icons
.
mic
,
color:
Pallete
.
firstSuggestionBoxColor
,
),
onPressed:
()
{
setState
(()
=>
_isVoiceMode
=
!
_isVoiceMode
);
},
),
if
(!
_isVoiceMode
)
IconButton
(
icon:
const
Icon
(
Icons
.
send
,
color:
Pallete
.
firstSuggestionBoxColor
,
),
onPressed:
_sendMessage
,
),
],
),
);
}
AppBar
_buildAppBar
()
{
return
AppBar
(
title:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
TextButton
(
onPressed:
()
{
setState
(()
=>
_currentIndex
=
0
);
},
child:
Text
(
'对话'
,
style:
TextStyle
(
fontSize:
_currentIndex
==
0
?
20
:
16
,
fontWeight:
_currentIndex
==
0
?
FontWeight
.
bold
:
FontWeight
.
normal
,
color:
_currentIndex
==
0
?
Pallete
.
firstSuggestionBoxColor
:
Colors
.
grey
,
),
),
),
const
SizedBox
(
width:
20
),
TextButton
(
onPressed:
()
{
setState
(()
=>
_currentIndex
=
1
);
},
child:
Text
(
'应用'
,
style:
TextStyle
(
fontSize:
_currentIndex
==
1
?
20
:
16
,
fontWeight:
_currentIndex
==
1
?
FontWeight
.
bold
:
FontWeight
.
normal
,
color:
_currentIndex
==
1
?
Pallete
.
firstSuggestionBoxColor
:
Colors
.
grey
,
),
),
),
],
),
leading:
Builder
(
builder:
(
context
)
=>
IconButton
(
icon:
const
Icon
(
Icons
.
menu
),
onPressed:
()
=>
Scaffold
.
of
(
context
).
openDrawer
(),
),
),
);
}
Widget
_buildDrawer
(
BuildContext
context
)
{
return
Drawer
(
child:
Column
(
children:
[
DrawerHeader
(
decoration:
BoxDecoration
(
color:
Pallete
.
firstSuggestionBoxColor
,
),
child:
const
Center
(
child:
Text
(
'会话列表'
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
24
,
),
),
),
),
ListTile
(
leading:
const
Icon
(
Icons
.
add
),
title:
const
Text
(
'新建会话'
),
onTap:
()
{
_createNewConversation
();
Navigator
.
pop
(
context
);
},
),
Expanded
(
child:
ListView
.
builder
(
itemCount:
_conversations
.
length
,
itemBuilder:
(
context
,
index
)
{
final
conversation
=
_conversations
[
index
];
return
ListTile
(
leading:
const
Icon
(
Icons
.
chat
),
title:
Text
(
conversation
.
title
),
subtitle:
Text
(
conversation
.
messages
.
isEmpty
?
'暂无消息'
:
conversation
.
messages
.
last
.
text
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
),
selected:
_currentConversation
.
id
==
conversation
.
id
,
onTap:
()
{
setState
(()
{
_currentConversation
=
conversation
;
messages
=
conversation
.
messages
;
});
Navigator
.
pop
(
context
);
},
trailing:
IconButton
(
icon:
const
Icon
(
Icons
.
delete
),
onPressed:
()
async
{
await
_storageService
.
deleteConversation
(
conversation
.
id
);
setState
(()
{
_conversations
.
removeAt
(
index
);
if
(
_currentConversation
.
id
==
conversation
.
id
)
{
if
(
_conversations
.
isEmpty
)
{
_createNewConversation
();
}
else
{
_currentConversation
=
_conversations
.
first
;
messages
=
_currentConversation
.
messages
;
}
}
});
},
),
);
},
),
),
const
Spacer
(),
const
Divider
(
height:
1
),
FutureBuilder
<
User
?>(
future:
StorageService
.
getUser
(),
builder:
(
context
,
snapshot
)
{
if
(!
snapshot
.
hasData
)
return
const
SizedBox
();
return
Container
(
padding:
const
EdgeInsets
.
all
(
16
),
decoration:
BoxDecoration
(
color:
Colors
.
grey
[
50
],
),
child:
Column
(
children:
[
Row
(
children:
[
Container
(
width:
50
,
height:
50
,
decoration:
BoxDecoration
(
color:
Pallete
.
firstSuggestionBoxColor
,
shape:
BoxShape
.
circle
,
),
child:
Center
(
child:
Text
(
snapshot
.
data
!.
username
[
0
].
toUpperCase
(),
style:
const
TextStyle
(
color:
Colors
.
white
,
fontSize:
24
,
fontWeight:
FontWeight
.
bold
,
),
),
),
),
const
SizedBox
(
width:
16
),
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
snapshot
.
data
!.
username
,
style:
const
TextStyle
(
fontSize:
18
,
fontWeight:
FontWeight
.
bold
,
),
),
const
SizedBox
(
height:
4
),
Text
(
'在线'
,
style:
TextStyle
(
fontSize:
14
,
color:
Colors
.
green
[
600
],
),
),
],
),
),
],
),
const
SizedBox
(
height:
16
),
InkWell
(
onTap:
()
async
{
final
confirmed
=
await
showDialog
<
bool
>(
context:
context
,
builder:
(
context
)
=>
AlertDialog
(
title:
const
Text
(
'确认退出'
),
content:
const
Text
(
'您确定要退出登录吗?'
),
actions:
[
TextButton
(
onPressed:
()
=>
Navigator
.
pop
(
context
,
false
),
child:
const
Text
(
'取消'
),
),
TextButton
(
onPressed:
()
=>
Navigator
.
pop
(
context
,
true
),
child:
const
Text
(
'退出'
,
style:
TextStyle
(
color:
Colors
.
red
),
),
),
],
),
);
if
(
confirmed
==
true
&&
context
.
mounted
)
{
await
StorageService
.
clearUser
();
Navigator
.
of
(
context
).
pushReplacementNamed
(
'/login'
);
}
},
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
vertical:
12
,
horizontal:
16
,
),
decoration:
BoxDecoration
(
color:
Colors
.
red
[
50
],
borderRadius:
BorderRadius
.
circular
(
8
),
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
Icon
(
Icons
.
logout
,
color:
Colors
.
red
[
700
],
size:
20
,
),
const
SizedBox
(
width:
8
),
Text
(
'出登录'
,
style:
TextStyle
(
color:
Colors
.
red
[
700
],
fontSize:
16
,
fontWeight:
FontWeight
.
w500
,
),
),
],
),
),
),
],
),
);
},
),
],
),
);
}
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
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
appBar:
widget
.
hideNavigation
?
null
:
_buildAppBar
(),
drawer:
widget
.
hideNavigation
?
null
:
_buildDrawer
(
context
),
body:
IndexedStack
(
index:
_currentIndex
,
children:
[
Column
(
children:
[
Expanded
(
child:
ListView
.
builder
(
padding:
const
EdgeInsets
.
all
(
16
),
itemCount:
messages
.
isEmpty
?
1
:
messages
.
length
,
itemBuilder:
(
context
,
index
)
{
if
(
messages
.
isEmpty
)
{
return
Center
(
child:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
ZoomIn
(
child:
Stack
(
children:
[
Center
(
child:
Container
(
height:
120
,
width:
120
,
margin:
const
EdgeInsets
.
only
(
top:
4
),
decoration:
const
BoxDecoration
(
color:
Pallete
.
assistantCircleColor
,
shape:
BoxShape
.
circle
,
),
),
),
Container
(
height:
123
,
decoration:
BoxDecoration
(
shape:
BoxShape
.
circle
,
image:
DecorationImage
(
image:
widget
.
customImageUrl
!=
null
?
NetworkImage
(
widget
.
customImageUrl
!)
:
const
AssetImage
(
'assets/images/virtualAssistant.png'
,
)
as
ImageProvider
,
onError:
(
exception
,
stackTrace
)
{
print
(
'Error loading image:
$exception
'
);
},
),
),
),
],
),
),
const
SizedBox
(
height:
20
),
Text
(
widget
.
customDescription
??
'你好!我是你的快际新云AI助手,请问有什么可以帮你的吗?'
,
style:
const
TextStyle
(
fontSize:
20
,
color:
Pallete
.
mainFontColor
,
),
textAlign:
TextAlign
.
center
,
),
],
),
);
}
final
message
=
messages
[
index
];
return
Column
(
children:
[
ChatBubble
(
message:
message
,
isTyping:
_isLoading
&&
index
==
messages
.
length
-
1
&&
!
message
.
isUserMessage
,
),
if
(
_isLoading
&&
index
==
messages
.
length
-
1
&&
message
.
isUserMessage
)
Padding
(
padding:
const
EdgeInsets
.
all
(
8.0
),
child:
Align
(
alignment:
Alignment
.
centerLeft
,
child:
Row
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
SizedBox
(
width:
20
,
height:
20
,
child:
CircularProgressIndicator
(
strokeWidth:
2
,
valueColor:
AlwaysStoppedAnimation
<
Color
>(
Pallete
.
firstSuggestionBoxColor
,
),
),
),
const
SizedBox
(
width:
8
),
const
Text
(
'正在思考中...'
,
style:
TextStyle
(
color:
Pallete
.
mainFontColor
,
fontSize:
14
,
),
),
],
),
),
),
],
);
},
),
),
_buildBottomInput
(),
],
),
if
(!
widget
.
hideNavigation
)
AppsPage
(),
],
),
);
}
}
\ No newline at end of file
import
'package:allen/pallete.dart'
;
import
'package:animate_do/animate_do.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_tts/flutter_tts.dart'
;
import
'package:speech_to_text/speech_recognition_result.dart'
;
import
'package:speech_to_text/speech_to_text.dart'
;
import
'package:flutter/foundation.dart'
show
kIsWeb
;
import
'package:uuid/uuid.dart'
;
import
'models/conversation.dart'
;
import
'services/storage_service.dart'
;
import
'services/chat_service.dart'
;
import
'models/chat_message.dart'
;
import
'models/user.dart'
;
import
'package:shared_preferences/shared_preferences.dart'
;
import
'pages/apps_page.dart'
;
import
'pages/chat_bubble.dart'
;
class
HomePage
extends
StatefulWidget
{
final
String
?
customTitle
;
final
String
?
customDescription
;
final
String
?
customImageUrl
;
final
bool
hideNavigation
;
final
OpenAIService
?
openAIService
;
const
HomePage
({
super
.
key
,
this
.
customTitle
,
this
.
customDescription
,
this
.
customImageUrl
,
this
.
hideNavigation
=
false
,
this
.
openAIService
,
});
@override
State
<
HomePage
>
createState
()
=>
_HomePageState
();
}
class
_HomePageState
extends
State
<
HomePage
>
{
final
speechToText
=
SpeechToText
();
final
flutterTts
=
FlutterTts
();
String
lastWords
=
''
;
late
OpenAIService
openAIService
;
String
?
generatedContent
;
String
?
generatedImageUrl
;
int
start
=
200
;
int
delay
=
200
;
final
TextEditingController
_messageController
=
TextEditingController
();
String
currentStreamedContent
=
''
;
List
<
ChatMessage
>
messages
=
[];
bool
_isLoading
=
false
;
bool
_isVoiceMode
=
true
;
bool
_isListeningPressed
=
false
;
String
_currentVoiceText
=
''
;
late
StorageService
_storageService
;
late
SharedPreferences
_prefs
;
late
Conversation
_currentConversation
;
List
<
Conversation
>
_conversations
=
[];
int
_currentIndex
=
0
;
bool
_hasSpeechPermission
=
false
;
@override
void
initState
()
{
super
.
initState
();
_initializeStorage
();
initSpeechToText
();
_initTts
();
openAIService
=
widget
.
openAIService
??
OpenAIService
();
}
Future
<
void
>
_initializeStorage
()
async
{
_prefs
=
await
SharedPreferences
.
getInstance
();
_storageService
=
StorageService
(
_prefs
);
_conversations
=
await
_storageService
.
getConversations
();
// 每次进入应用创建一个新的会话
_createNewConversation
();
// if (_conversations.isEmpty) {
// _createNewConversation();
// } else {
// _currentConversation = _conversations.first;
// setState(() {
// messages = _currentConversation.messages;
// });
// }
}
Future
<
void
>
_createNewConversation
()
async
{
final
newConversation
=
Conversation
(
id:
const
Uuid
().
v4
(),
title:
'新会话
${_conversations.length + 1}
'
,
createdAt:
DateTime
.
now
(),
messages:
[],
);
await
_storageService
.
addConversation
(
newConversation
);
setState
(()
{
_conversations
.
insert
(
0
,
newConversation
);
_currentConversation
=
newConversation
;
messages
=
[];
});
}
Future
<
void
>
_updateCurrentConversation
()
async
{
_currentConversation
=
Conversation
(
id:
_currentConversation
.
id
,
title:
_currentConversation
.
title
,
createdAt:
_currentConversation
.
createdAt
,
messages:
messages
,
);
await
_storageService
.
updateConversation
(
_currentConversation
);
}
Future
<
void
>
_initTts
()
async
{
if
(!
kIsWeb
)
{
await
flutterTts
.
setSharedInstance
(
true
);
}
setState
(()
{});
}
Future
<
void
>
initSpeechToText
()
async
{
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
(()
{});
}
Future
<
void
>
startListening
()
async
{
if
(!
_hasSpeechPermission
)
{
_showErrorDialog
(
'请先授予麦克风权限'
);
setState
(()
=>
_isListeningPressed
=
false
);
return
;
}
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
{
await
speechToText
.
stop
();
setState
(()
{});
}
Future
<
void
>
onSpeechResult
(
SpeechRecognitionResult
result
)
async
{
setState
(()
{
lastWords
=
result
.
recognizedWords
;
_currentVoiceText
=
result
.
recognizedWords
;
});
}
Future
<
void
>
systemSpeak
(
String
content
)
async
{
try
{
if
(
kIsWeb
)
{
// 设置语速
await
flutterTts
.
setSpeechRate
(
1
);
// 音调
await
flutterTts
.
setPitch
(
0.8
);
await
flutterTts
.
speak
(
content
);
}
else
{
await
flutterTts
.
setSharedInstance
(
true
);
await
flutterTts
.
speak
(
content
);
}
}
catch
(
e
)
{
print
(
'TTS Error:
$e
'
);
}
}
Future
<
void
>
_sendMessage
()
async
{
if
(!
mounted
)
return
;
// 添加这行
if
(
_messageController
.
text
.
isEmpty
)
return
;
String
userMessage
=
_messageController
.
text
;
_messageController
.
clear
();
setState
(()
{
messages
.
add
(
ChatMessage
(
text:
userMessage
,
isUserMessage:
true
,
));
currentStreamedContent
=
''
;
_isLoading
=
true
;
});
try
{
String
fullResponse
=
''
;
bool
isFirstChunk
=
true
;
// 创建一个缓冲区来存储收到的文本
StringBuffer
buffer
=
StringBuffer
();
await
for
(
final
chunk
in
openAIService
.
chatGPTAPI
(
userMessage
))
{
buffer
.
write
(
chunk
);
// 逐字显示文本
for
(
int
i
=
fullResponse
.
length
;
i
<
buffer
.
length
;
i
++)
{
setState
(()
{
fullResponse
+=
buffer
.
toString
()[
i
];
if
(
isFirstChunk
&&
i
==
0
)
{
messages
.
add
(
ChatMessage
(
text:
fullResponse
,
isUserMessage:
false
,
));
isFirstChunk
=
false
;
}
else
{
messages
.
last
=
ChatMessage
(
text:
fullResponse
,
isUserMessage:
false
,
);
}
});
// 添加短暂延迟以创建打字效果
await
Future
.
delayed
(
const
Duration
(
milliseconds:
50
));
}
}
//await systemSpeak(fullResponse);
await
_updateCurrentConversation
();
}
catch
(
e
)
{
setState
(()
{
messages
.
add
(
ChatMessage
(
text:
'抱歉,出现了一些错误:
$e
'
,
isUserMessage:
false
,
));
});
}
finally
{
setState
(()
{
_isLoading
=
false
;
});
}
}
Future
<
void
>
_processAIResponse
(
String
userInput
)
async
{
try
{
String
fullResponse
=
''
;
bool
isFirstChunk
=
true
;
// 创建一个缓冲区来储收到的文本
StringBuffer
buffer
=
StringBuffer
();
await
for
(
final
chunk
in
openAIService
.
chatGPTAPI
(
userInput
))
{
buffer
.
write
(
chunk
);
// 逐字显示文本
for
(
int
i
=
fullResponse
.
length
;
i
<
buffer
.
length
;
i
++)
{
setState
(()
{
fullResponse
+=
buffer
.
toString
()[
i
];
if
(
isFirstChunk
&&
i
==
0
)
{
messages
.
add
(
ChatMessage
(
text:
fullResponse
,
isUserMessage:
false
,
));
isFirstChunk
=
false
;
}
else
{
messages
.
last
=
ChatMessage
(
text:
fullResponse
,
isUserMessage:
false
,
);
}
});
// 添加短暂延迟以创建打字效果
await
Future
.
delayed
(
const
Duration
(
milliseconds:
50
));
}
}
// await systemSpeak(fullResponse);
await
_updateCurrentConversation
();
}
catch
(
e
)
{
setState
(()
{
messages
.
add
(
ChatMessage
(
text:
'抱歉,出现了一些错误:
$e
'
,
isUserMessage:
false
,
));
});
}
finally
{
setState
(()
{
_isLoading
=
false
;
});
}
}
@override
void
dispose
()
{
super
.
dispose
();
speechToText
.
stop
();
flutterTts
.
stop
();
_messageController
.
dispose
();
}
Widget
_buildBottomInput
()
{
return
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
16
,
vertical:
8
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
boxShadow:
[
BoxShadow
(
color:
Colors
.
grey
.
withOpacity
(
0.1
),
spreadRadius:
1
,
blurRadius:
3
,
offset:
const
Offset
(
0
,
-
1
),
),
],
),
child:
Row
(
children:
[
Expanded
(
child:
_isVoiceMode
?
GestureDetector
(
onTap:
()
async
{
if
(!
_isListeningPressed
)
{
// 开始录音
setState
(()
{
_isListeningPressed
=
true
;
_currentVoiceText
=
''
;
});
await
startListening
();
}
else
{
// 停止录音
setState
(()
=>
_isListeningPressed
=
false
);
await
stopListening
();
// 确保有语音文本才处理
if
(
_currentVoiceText
.
isNotEmpty
)
{
setState
(()
{
messages
.
add
(
ChatMessage
(
text:
_currentVoiceText
,
isUserMessage:
true
,
));
_isLoading
=
true
;
});
await
_processAIResponse
(
_currentVoiceText
);
}
setState
(()
=>
_currentVoiceText
=
''
);
}
},
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
20
,
vertical:
10
,
),
decoration:
BoxDecoration
(
color:
Colors
.
grey
[
100
],
borderRadius:
BorderRadius
.
circular
(
25
),
),
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
?
(
_currentVoiceText
.
isEmpty
?
'点击停止'
:
_currentVoiceText
)
:
'点击说话'
,
textAlign:
TextAlign
.
center
,
style:
TextStyle
(
color:
_isListeningPressed
?
Pallete
.
firstSuggestionBoxColor
:
Colors
.
grey
[
600
],
),
),
],
),
),
)
:
TextField
(
controller:
_messageController
,
decoration:
InputDecoration
(
hintText:
'输入消息...'
,
border:
OutlineInputBorder
(
borderRadius:
BorderRadius
.
circular
(
25
),
borderSide:
BorderSide
.
none
,
),
filled:
true
,
fillColor:
Colors
.
grey
[
100
],
contentPadding:
const
EdgeInsets
.
symmetric
(
horizontal:
20
,
vertical:
10
,
),
),
),
),
const
SizedBox
(
width:
8
),
IconButton
(
icon:
Icon
(
_isVoiceMode
?
Icons
.
keyboard
:
Icons
.
mic
,
color:
Pallete
.
firstSuggestionBoxColor
,
),
onPressed:
()
{
setState
(()
=>
_isVoiceMode
=
!
_isVoiceMode
);
},
),
if
(!
_isVoiceMode
)
IconButton
(
icon:
const
Icon
(
Icons
.
send
,
color:
Pallete
.
firstSuggestionBoxColor
,
),
onPressed:
_sendMessage
,
),
],
),
);
}
AppBar
_buildAppBar
()
{
return
AppBar
(
title:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
TextButton
(
onPressed:
()
{
setState
(()
=>
_currentIndex
=
0
);
},
child:
Text
(
'对话'
,
style:
TextStyle
(
fontSize:
_currentIndex
==
0
?
20
:
16
,
fontWeight:
_currentIndex
==
0
?
FontWeight
.
bold
:
FontWeight
.
normal
,
color:
_currentIndex
==
0
?
Pallete
.
firstSuggestionBoxColor
:
Colors
.
grey
,
),
),
),
const
SizedBox
(
width:
20
),
TextButton
(
onPressed:
()
{
setState
(()
=>
_currentIndex
=
1
);
},
child:
Text
(
'应用'
,
style:
TextStyle
(
fontSize:
_currentIndex
==
1
?
20
:
16
,
fontWeight:
_currentIndex
==
1
?
FontWeight
.
bold
:
FontWeight
.
normal
,
color:
_currentIndex
==
1
?
Pallete
.
firstSuggestionBoxColor
:
Colors
.
grey
,
),
),
),
],
),
leading:
Builder
(
builder:
(
context
)
=>
IconButton
(
icon:
const
Icon
(
Icons
.
menu
),
onPressed:
()
=>
Scaffold
.
of
(
context
).
openDrawer
(),
),
),
);
}
Widget
_buildDrawer
(
BuildContext
context
)
{
return
Drawer
(
child:
Column
(
children:
[
DrawerHeader
(
decoration:
BoxDecoration
(
color:
Pallete
.
firstSuggestionBoxColor
,
),
child:
const
Center
(
child:
Text
(
'会话列表'
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
24
,
),
),
),
),
ListTile
(
leading:
const
Icon
(
Icons
.
add
),
title:
const
Text
(
'新建会话'
),
onTap:
()
{
_createNewConversation
();
Navigator
.
pop
(
context
);
},
),
Expanded
(
child:
ListView
.
builder
(
itemCount:
_conversations
.
length
,
itemBuilder:
(
context
,
index
)
{
final
conversation
=
_conversations
[
index
];
return
ListTile
(
leading:
const
Icon
(
Icons
.
chat
),
title:
Text
(
conversation
.
title
),
subtitle:
Text
(
conversation
.
messages
.
isEmpty
?
'暂无消息'
:
conversation
.
messages
.
last
.
text
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
),
selected:
_currentConversation
.
id
==
conversation
.
id
,
onTap:
()
{
setState
(()
{
_currentConversation
=
conversation
;
messages
=
conversation
.
messages
;
});
Navigator
.
pop
(
context
);
},
trailing:
IconButton
(
icon:
const
Icon
(
Icons
.
delete
),
onPressed:
()
async
{
await
_storageService
.
deleteConversation
(
conversation
.
id
);
setState
(()
{
_conversations
.
removeAt
(
index
);
if
(
_currentConversation
.
id
==
conversation
.
id
)
{
if
(
_conversations
.
isEmpty
)
{
_createNewConversation
();
}
else
{
_currentConversation
=
_conversations
.
first
;
messages
=
_currentConversation
.
messages
;
}
}
});
},
),
);
},
),
),
const
Spacer
(),
const
Divider
(
height:
1
),
FutureBuilder
<
User
?>(
future:
StorageService
.
getUser
(),
builder:
(
context
,
snapshot
)
{
if
(!
snapshot
.
hasData
)
return
const
SizedBox
();
return
Container
(
padding:
const
EdgeInsets
.
all
(
16
),
decoration:
BoxDecoration
(
color:
Colors
.
grey
[
50
],
),
child:
Column
(
children:
[
Row
(
children:
[
Container
(
width:
50
,
height:
50
,
decoration:
BoxDecoration
(
color:
Pallete
.
firstSuggestionBoxColor
,
shape:
BoxShape
.
circle
,
),
child:
Center
(
child:
Text
(
snapshot
.
data
!.
username
[
0
].
toUpperCase
(),
style:
const
TextStyle
(
color:
Colors
.
white
,
fontSize:
24
,
fontWeight:
FontWeight
.
bold
,
),
),
),
),
const
SizedBox
(
width:
16
),
Expanded
(
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
snapshot
.
data
!.
username
,
style:
const
TextStyle
(
fontSize:
18
,
fontWeight:
FontWeight
.
bold
,
),
),
const
SizedBox
(
height:
4
),
Text
(
'在线'
,
style:
TextStyle
(
fontSize:
14
,
color:
Colors
.
green
[
600
],
),
),
],
),
),
],
),
const
SizedBox
(
height:
16
),
InkWell
(
onTap:
()
async
{
final
confirmed
=
await
showDialog
<
bool
>(
context:
context
,
builder:
(
context
)
=>
AlertDialog
(
title:
const
Text
(
'确认退出'
),
content:
const
Text
(
'您确定要退出登录吗?'
),
actions:
[
TextButton
(
onPressed:
()
=>
Navigator
.
pop
(
context
,
false
),
child:
const
Text
(
'取消'
),
),
TextButton
(
onPressed:
()
=>
Navigator
.
pop
(
context
,
true
),
child:
const
Text
(
'退出'
,
style:
TextStyle
(
color:
Colors
.
red
),
),
),
],
),
);
if
(
confirmed
==
true
&&
context
.
mounted
)
{
await
StorageService
.
clearUser
();
Navigator
.
of
(
context
).
pushReplacementNamed
(
'/login'
);
}
},
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
vertical:
12
,
horizontal:
16
,
),
decoration:
BoxDecoration
(
color:
Colors
.
red
[
50
],
borderRadius:
BorderRadius
.
circular
(
8
),
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
Icon
(
Icons
.
logout
,
color:
Colors
.
red
[
700
],
size:
20
,
),
const
SizedBox
(
width:
8
),
Text
(
'出登录'
,
style:
TextStyle
(
color:
Colors
.
red
[
700
],
fontSize:
16
,
fontWeight:
FontWeight
.
w500
,
),
),
],
),
),
),
],
),
);
},
),
],
),
);
}
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
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
appBar:
widget
.
hideNavigation
?
null
:
_buildAppBar
(),
drawer:
widget
.
hideNavigation
?
null
:
_buildDrawer
(
context
),
body:
IndexedStack
(
index:
_currentIndex
,
children:
[
Column
(
children:
[
Expanded
(
child:
ListView
.
builder
(
padding:
const
EdgeInsets
.
all
(
16
),
itemCount:
messages
.
isEmpty
?
1
:
messages
.
length
,
itemBuilder:
(
context
,
index
)
{
if
(
messages
.
isEmpty
)
{
return
Center
(
child:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
ZoomIn
(
child:
Stack
(
children:
[
Center
(
child:
Container
(
height:
120
,
width:
120
,
margin:
const
EdgeInsets
.
only
(
top:
4
),
decoration:
const
BoxDecoration
(
color:
Pallete
.
assistantCircleColor
,
shape:
BoxShape
.
circle
,
),
),
),
Container
(
height:
123
,
decoration:
BoxDecoration
(
shape:
BoxShape
.
circle
,
image:
DecorationImage
(
image:
widget
.
customImageUrl
!=
null
?
NetworkImage
(
widget
.
customImageUrl
!)
:
const
AssetImage
(
'assets/images/virtualAssistant.png'
,
)
as
ImageProvider
,
onError:
(
exception
,
stackTrace
)
{
print
(
'Error loading image:
$exception
'
);
},
),
),
),
],
),
),
const
SizedBox
(
height:
20
),
Text
(
widget
.
customDescription
??
'你好!我是你的快际新云AI助手,请问有什么可以帮你的吗?'
,
style:
const
TextStyle
(
fontSize:
20
,
color:
Pallete
.
mainFontColor
,
),
textAlign:
TextAlign
.
center
,
),
],
),
);
}
final
message
=
messages
[
index
];
return
Column
(
children:
[
ChatBubble
(
message:
message
,
isTyping:
_isLoading
&&
index
==
messages
.
length
-
1
&&
!
message
.
isUserMessage
,
),
if
(
_isLoading
&&
index
==
messages
.
length
-
1
&&
message
.
isUserMessage
)
Padding
(
padding:
const
EdgeInsets
.
all
(
8.0
),
child:
Align
(
alignment:
Alignment
.
centerLeft
,
child:
Row
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
SizedBox
(
width:
20
,
height:
20
,
child:
CircularProgressIndicator
(
strokeWidth:
2
,
valueColor:
AlwaysStoppedAnimation
<
Color
>(
Pallete
.
firstSuggestionBoxColor
,
),
),
),
const
SizedBox
(
width:
8
),
const
Text
(
'正在思考中...'
,
style:
TextStyle
(
color:
Pallete
.
mainFontColor
,
fontSize:
14
,
),
),
],
),
),
),
],
);
},
),
),
_buildBottomInput
(),
],
),
if
(!
widget
.
hideNavigation
)
AppsPage
(),
],
),
);
}
}
lib/pages/chat_page.dart
浏览文件 @
c2c63647
...
...
@@ -6,6 +6,7 @@ import 'package:shared_preferences/shared_preferences.dart';
import
'../models/app_item.dart'
;
import
'../services/chat_service.dart'
;
import
'../services/storage_service.dart'
;
import
'../home_page.dart'
;
...
...
@@ -22,6 +23,8 @@ class ChatPage extends ConsumerStatefulWidget {
}
class
_ChatPageState
extends
ConsumerState
<
ChatPage
>
{
final
OpenAIService
openAIService
=
OpenAIService
();
@override
void
initState
()
{
super
.
initState
();
...
...
@@ -46,6 +49,12 @@ class _ChatPageState extends ConsumerState<ChatPage> {
}
@override
void
dispose
()
{
openAIService
.
clearConversation
();
super
.
dispose
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
appBar:
AppBar
(
...
...
@@ -78,6 +87,7 @@ class _ChatPageState extends ConsumerState<ChatPage> {
customDescription:
widget
.
app
.
description
,
customImageUrl:
widget
.
app
.
iconUrl
,
hideNavigation:
true
,
openAIService:
openAIService
,
),
);
}
...
...
lib/services/chat_service.dart
浏览文件 @
c2c63647
...
...
@@ -8,6 +8,32 @@ class OpenAIService {
// final String apiKey = 'sk-OVjS7VE9mT68Uvg7kSFoMnbU6EU836FO';
// final String appKey = 'app-FRP2s2wSx01rsE67';
String
?
conversationId
;
bool
_isFirstMessage
=
true
;
Future
<
void
>
_generateConversationName
()
async
{
if
(
conversationId
==
null
)
return
;
var
prefs
=
await
SharedPreferences
.
getInstance
();
var
storageService
=
StorageService
(
prefs
);
String
appKey
=
storageService
.
getWorkbenchToken
()
??
''
;
try
{
final
response
=
await
http
.
post
(
Uri
.
parse
(
'
$baseUrl
/api/conversations/
$conversationId
/name'
),
headers:
{
'Content-Type'
:
'application/json'
,
'Authorization'
:
'Bearer
$appKey
'
,
},
body:
jsonEncode
({
"auto_generate"
:
true
}),
);
if
(
response
.
statusCode
!=
200
)
{
print
(
'生成会话名称失败:
${response.statusCode}
'
);
}
}
catch
(
e
)
{
print
(
'生成会话名称异常:
$e
'
);
}
}
Stream
<
String
>
chatGPTAPI
(
String
message
)
async
*
{
final
client
=
http
.
Client
();
...
...
@@ -85,11 +111,16 @@ class OpenAIService {
}
catch
(
e
)
{
throw
Exception
(
e
.
toString
());
}
finally
{
if
(
_isFirstMessage
&&
conversationId
!=
null
)
{
await
_generateConversationName
();
_isFirstMessage
=
false
;
}
client
.
close
();
}
}
void
clearConversation
()
{
conversationId
=
null
;
_isFirstMessage
=
true
;
}
}
macos/Flutter/GeneratedPluginRegistrant.swift
浏览文件 @
c2c63647
...
...
@@ -9,12 +9,12 @@ import audioplayers_darwin
import
flutter_tts
import
path_provider_foundation
import
shared_preferences_foundation
import
speech_to_text
_macos
import
speech_to_text
func
RegisterGeneratedPlugins
(
registry
:
FlutterPluginRegistry
)
{
AudioplayersDarwinPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"AudioplayersDarwinPlugin"
))
FlutterTtsPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"FlutterTtsPlugin"
))
PathProviderPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"PathProviderPlugin"
))
SharedPreferencesPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"SharedPreferencesPlugin"
))
SpeechToText
MacosPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"SpeechToTextMacos
Plugin"
))
SpeechToText
Plugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"SpeechToText
Plugin"
))
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论