|
|
|
@ -25,6 +25,9 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver { |
|
|
|
late MessageActionController messageActionController; |
|
|
|
double _keyboardHeight = 0; |
|
|
|
|
|
|
|
// 👇 新增:用于控制 TextField 焦点 |
|
|
|
final FocusNode _textFocusNode = FocusNode(); |
|
|
|
|
|
|
|
@override |
|
|
|
void initState() { |
|
|
|
super.initState(); |
|
|
|
@ -35,6 +38,7 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver { |
|
|
|
|
|
|
|
@override |
|
|
|
void dispose() { |
|
|
|
_textFocusNode.dispose(); |
|
|
|
WidgetsBinding.instance.removeObserver(this); |
|
|
|
super.dispose(); |
|
|
|
} |
|
|
|
@ -286,65 +290,102 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver { |
|
|
|
children: [ |
|
|
|
// 聊天内容 |
|
|
|
Expanded( |
|
|
|
child: Obx(() { |
|
|
|
return Align( |
|
|
|
alignment: Alignment.topCenter, |
|
|
|
child: ListView.builder( |
|
|
|
controller: controller.scrollController, |
|
|
|
shrinkWrap: true, |
|
|
|
reverse: true, |
|
|
|
padding: EdgeInsets.symmetric( |
|
|
|
vertical: 16.h, horizontal: 16.w), |
|
|
|
itemCount: controller.messages.length + |
|
|
|
(controller.hasMoreMessages.value ? 1 : 0), |
|
|
|
itemBuilder: (context, index) { |
|
|
|
// 如果是最后一项且有更多消息,显示加载指示器 |
|
|
|
if (index == controller.messages.length && |
|
|
|
controller.hasMoreMessages.value) { |
|
|
|
return _buildLoadMoreIndicator(); |
|
|
|
} |
|
|
|
child: GestureDetector( |
|
|
|
onTap: () { |
|
|
|
// 点击聊天区域时,收起键盘并切换工具显示状态 |
|
|
|
FocusScope.of(context).unfocus(); |
|
|
|
if (controller.isPlusOptionsExpanded.value) { |
|
|
|
controller.collapsePlusOptions(); |
|
|
|
} |
|
|
|
}, |
|
|
|
child: Obx(() { |
|
|
|
return Align( |
|
|
|
alignment: Alignment.topCenter, |
|
|
|
child: ListView.builder( |
|
|
|
controller: controller.scrollController, |
|
|
|
shrinkWrap: true, |
|
|
|
reverse: true, |
|
|
|
padding: EdgeInsets.symmetric( |
|
|
|
vertical: 16.h, horizontal: 16.w), |
|
|
|
itemCount: controller.messages.length + |
|
|
|
(controller.hasMoreMessages.value ? 1 : 0), |
|
|
|
itemBuilder: (context, index) { |
|
|
|
// 如果是最后一项且有更多消息,显示加载指示器 |
|
|
|
if (index == controller.messages.length && |
|
|
|
controller.hasMoreMessages.value) { |
|
|
|
return _buildLoadMoreIndicator(); |
|
|
|
} |
|
|
|
|
|
|
|
final message = controller |
|
|
|
.messages[controller.messages.length - 1 - index]; |
|
|
|
return index == 0 |
|
|
|
? IntroStepBuilder( |
|
|
|
group: 'ai', |
|
|
|
order: 4, |
|
|
|
getOverlayPosition: |
|
|
|
IntroOverlayBuilder.overlayPosition, |
|
|
|
overlayBuilder: (params) { |
|
|
|
return IntroOverlayBuilder.aiBuilder( |
|
|
|
params, |
|
|
|
controller.noviceGuideService.aiStepsList[ |
|
|
|
controller |
|
|
|
.noviceGuideService.aiStep]['end'], |
|
|
|
onFinish: () async { |
|
|
|
if (controller.noviceGuideService.aiStep < |
|
|
|
5) { |
|
|
|
await Future.delayed( |
|
|
|
const Duration(milliseconds: 300)); |
|
|
|
controller.noviceGuideService.aiStep++; |
|
|
|
Intro.of(Get.context!) |
|
|
|
.start(group: 'ai', reset: true); |
|
|
|
} |
|
|
|
}, |
|
|
|
onTap: () async { |
|
|
|
params.onFinish(); |
|
|
|
if (controller.noviceGuideService.aiStep < |
|
|
|
5) { |
|
|
|
await Future.delayed( |
|
|
|
const Duration(milliseconds: 300)); |
|
|
|
controller.noviceGuideService.aiStep++; |
|
|
|
Intro.of(Get.context!) |
|
|
|
.start(group: 'ai', reset: true); |
|
|
|
} |
|
|
|
}, |
|
|
|
); |
|
|
|
}, |
|
|
|
builder: |
|
|
|
(BuildContext context, GlobalKey key) => |
|
|
|
Padding( |
|
|
|
key: key, |
|
|
|
final message = controller |
|
|
|
.messages[controller.messages.length - 1 - index]; |
|
|
|
return index == 0 |
|
|
|
? IntroStepBuilder( |
|
|
|
group: 'ai', |
|
|
|
order: 4, |
|
|
|
getOverlayPosition: |
|
|
|
IntroOverlayBuilder.overlayPosition, |
|
|
|
overlayBuilder: (params) { |
|
|
|
return IntroOverlayBuilder.aiBuilder( |
|
|
|
params, |
|
|
|
controller.noviceGuideService.aiStepsList[ |
|
|
|
controller.noviceGuideService |
|
|
|
.aiStep]['end'], |
|
|
|
onFinish: () async { |
|
|
|
if (controller |
|
|
|
.noviceGuideService.aiStep < |
|
|
|
5) { |
|
|
|
await Future.delayed(const Duration( |
|
|
|
milliseconds: 300)); |
|
|
|
controller |
|
|
|
.noviceGuideService.aiStep++; |
|
|
|
Intro.of(Get.context!) |
|
|
|
.start(group: 'ai', reset: true); |
|
|
|
} |
|
|
|
}, |
|
|
|
onTap: () async { |
|
|
|
params.onFinish(); |
|
|
|
if (controller |
|
|
|
.noviceGuideService.aiStep < |
|
|
|
5) { |
|
|
|
await Future.delayed(const Duration( |
|
|
|
milliseconds: 300)); |
|
|
|
controller |
|
|
|
.noviceGuideService.aiStep++; |
|
|
|
Intro.of(Get.context!) |
|
|
|
.start(group: 'ai', reset: true); |
|
|
|
} |
|
|
|
}, |
|
|
|
); |
|
|
|
}, |
|
|
|
builder: |
|
|
|
(BuildContext context, GlobalKey key) => |
|
|
|
Padding( |
|
|
|
key: key, |
|
|
|
padding: EdgeInsets.only(bottom: 8.h), |
|
|
|
child: GestureDetector( |
|
|
|
onLongPressStart: |
|
|
|
(LongPressStartDetails details) { |
|
|
|
messageActionController |
|
|
|
.onLongPressMessage(context, |
|
|
|
isDarkMode, details, message); |
|
|
|
}, |
|
|
|
child: MessageBubble( |
|
|
|
sessionid: message.sessionid, |
|
|
|
isUser: message.isUser, |
|
|
|
message: message.text, |
|
|
|
timestamp: message.timestamp, |
|
|
|
isRecognizing: message.isRecognizing, |
|
|
|
hasImage: message.hasImage, |
|
|
|
imagePath: message.imagePath, |
|
|
|
meta: message.meta, |
|
|
|
functionCalls: message.functionCalls, |
|
|
|
isToolCallsExpanded: |
|
|
|
message.isToolCallsExpanded, |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
) |
|
|
|
: Padding( |
|
|
|
padding: EdgeInsets.only(bottom: 8.h), |
|
|
|
child: GestureDetector( |
|
|
|
onLongPressStart: |
|
|
|
@ -367,35 +408,12 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver { |
|
|
|
message.isToolCallsExpanded, |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
) |
|
|
|
: Padding( |
|
|
|
padding: EdgeInsets.only(bottom: 8.h), |
|
|
|
child: GestureDetector( |
|
|
|
onLongPressStart: |
|
|
|
(LongPressStartDetails details) { |
|
|
|
messageActionController.onLongPressMessage( |
|
|
|
context, isDarkMode, details, message); |
|
|
|
}, |
|
|
|
child: MessageBubble( |
|
|
|
sessionid: message.sessionid, |
|
|
|
isUser: message.isUser, |
|
|
|
message: message.text, |
|
|
|
timestamp: message.timestamp, |
|
|
|
isRecognizing: message.isRecognizing, |
|
|
|
hasImage: message.hasImage, |
|
|
|
imagePath: message.imagePath, |
|
|
|
meta: message.meta, |
|
|
|
functionCalls: message.functionCalls, |
|
|
|
isToolCallsExpanded: |
|
|
|
message.isToolCallsExpanded, |
|
|
|
), |
|
|
|
), |
|
|
|
); |
|
|
|
}, |
|
|
|
), |
|
|
|
); |
|
|
|
}), |
|
|
|
); |
|
|
|
}, |
|
|
|
), |
|
|
|
); |
|
|
|
}), |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
// 输入区域 |
|
|
|
@ -553,6 +571,13 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver { |
|
|
|
return GestureDetector( |
|
|
|
onTap: () { |
|
|
|
controller.toggleInputMode(); |
|
|
|
// 切换模式时,确保焦点正确 |
|
|
|
if (!isTextMode) { |
|
|
|
// 切换到语音模式时,请求焦点 |
|
|
|
Future.delayed(Duration.zero, () { |
|
|
|
_textFocusNode.requestFocus(); |
|
|
|
}); |
|
|
|
} |
|
|
|
}, |
|
|
|
child: Container( |
|
|
|
width: 44.w, |
|
|
|
@ -652,6 +677,7 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver { |
|
|
|
controller.isImageInputActive.value; |
|
|
|
|
|
|
|
return TextField( |
|
|
|
focusNode: _textFocusNode, // 绑定焦点节点 |
|
|
|
controller: controller.textController, |
|
|
|
maxLines: isImageActive |
|
|
|
? 3 |
|
|
|
|