Browse Source

feat(agent_view): 添加聊天区域点击收起键盘功能并优化焦点控制

- 新增 FocusNode 用于控制 TextField 焦点
- 点击聊天区域时收起键盘并收起扩展选项
- 切换输入模式时自动管理焦点状态
newdev_shunjiawei
lxm 12 months ago
parent
commit
a5c10b6ce3
  1. 200
      lib/modules/agent/views/agent_view.dart

200
lib/modules/agent/views/agent_view.dart

@ -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

Loading…
Cancel
Save