|
|
|
@ -11,12 +11,45 @@ import '../../../core/theme/app_colors.dart'; |
|
|
|
import 'package:haptic_feedback/haptic_feedback.dart'; |
|
|
|
import '../../../data/models/message_model.dart'; |
|
|
|
|
|
|
|
class AgentView extends StatelessWidget { |
|
|
|
class AgentView extends StatefulWidget { |
|
|
|
const AgentView({Key? key}) : super(key: key); |
|
|
|
|
|
|
|
@override |
|
|
|
State<AgentView> createState() => _AgentViewState(); |
|
|
|
} |
|
|
|
|
|
|
|
class _AgentViewState extends State<AgentView> with WidgetsBindingObserver { |
|
|
|
late AgentController controller; |
|
|
|
double _keyboardHeight = 0; |
|
|
|
|
|
|
|
@override |
|
|
|
void initState() { |
|
|
|
super.initState(); |
|
|
|
controller = Get.find<AgentController>(); |
|
|
|
WidgetsBinding.instance.addObserver(this); |
|
|
|
} |
|
|
|
|
|
|
|
@override |
|
|
|
void dispose() { |
|
|
|
WidgetsBinding.instance.removeObserver(this); |
|
|
|
super.dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
@override |
|
|
|
void didChangeMetrics() { |
|
|
|
super.didChangeMetrics(); |
|
|
|
final bottomInset = View.of(context).viewInsets.bottom; |
|
|
|
final newKeyboardHeight = bottomInset / View.of(context).devicePixelRatio; |
|
|
|
|
|
|
|
if (_keyboardHeight != newKeyboardHeight) { |
|
|
|
_keyboardHeight = newKeyboardHeight; |
|
|
|
// 更新键盘可见状态 |
|
|
|
controller.updateKeyboardVisibility(_keyboardHeight > 0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@override |
|
|
|
Widget build(BuildContext context) { |
|
|
|
final controller = Get.find<AgentController>(); |
|
|
|
final theme = Theme.of(context); |
|
|
|
final primaryColor = theme.primaryColor; |
|
|
|
final backgroundColor = theme.scaffoldBackgroundColor; |
|
|
|
@ -288,333 +321,391 @@ class AgentView extends StatelessWidget { |
|
|
|
} |
|
|
|
}), |
|
|
|
|
|
|
|
// 输入控件行 - 使用IntrinsicHeight确保按钮与输入框底部对齐 |
|
|
|
IntrinsicHeight( |
|
|
|
child: Row( |
|
|
|
crossAxisAlignment: CrossAxisAlignment.end, // 所有元素底部对齐 |
|
|
|
children: [ |
|
|
|
// 键盘/语音切换按钮 |
|
|
|
Obx(() { |
|
|
|
final isTextMode = controller.isTextInputMode.value; |
|
|
|
return GestureDetector( |
|
|
|
onTap: () { |
|
|
|
controller.toggleInputMode(); |
|
|
|
}, |
|
|
|
child: Container( |
|
|
|
width: 44, |
|
|
|
height: 44, |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[800] |
|
|
|
: Colors.grey[200], |
|
|
|
borderRadius: BorderRadius.circular(22), |
|
|
|
), |
|
|
|
child: Icon( |
|
|
|
isTextMode ? Icons.mic_none : Icons.keyboard, |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[300] |
|
|
|
: Colors.grey[700], |
|
|
|
size: 22, |
|
|
|
), |
|
|
|
), |
|
|
|
); |
|
|
|
}), |
|
|
|
|
|
|
|
const SizedBox(width: 8), |
|
|
|
|
|
|
|
// 文本输入框或语音波形图 |
|
|
|
Expanded( |
|
|
|
child: Obx(() { |
|
|
|
final isTextMode = controller.isTextInputMode.value; |
|
|
|
|
|
|
|
if (isTextMode) { |
|
|
|
// 文本输入模式 - 限制最大高度并只向上扩展 |
|
|
|
return Container( |
|
|
|
constraints: const BoxConstraints( |
|
|
|
minHeight: 44, // 最小高度 |
|
|
|
maxHeight: 120, // 最大高度限制,防止挤压按钮 |
|
|
|
), |
|
|
|
padding: |
|
|
|
const EdgeInsets.symmetric(horizontal: 16), |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[800] |
|
|
|
: Colors.grey[200], |
|
|
|
borderRadius: BorderRadius.circular(22), |
|
|
|
border: Border.all( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[ |
|
|
|
800]! // 修改: 从 Colors.grey[700] 改为 Colors.grey[800] |
|
|
|
: Colors.grey.withOpacity(0.1), |
|
|
|
width: 1.0, |
|
|
|
), |
|
|
|
), |
|
|
|
child: Row( |
|
|
|
crossAxisAlignment: |
|
|
|
CrossAxisAlignment.end, // 对齐到底部,实现向上扩展 |
|
|
|
children: [ |
|
|
|
Expanded( |
|
|
|
child: Obx(() { |
|
|
|
// 图片模式下使用更高的输入框 |
|
|
|
final isImageActive = |
|
|
|
controller.isImageInputActive.value; |
|
|
|
|
|
|
|
return TextField( |
|
|
|
controller: controller.textController, |
|
|
|
maxLines: |
|
|
|
isImageActive ? 3 : 5, // 设置合理的最大行数 |
|
|
|
minLines: isImageActive ? 2 : 1, |
|
|
|
keyboardType: |
|
|
|
TextInputType.multiline, // 启用多行键盘 |
|
|
|
textInputAction: |
|
|
|
TextInputAction.newline, // 支持回车换行 |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 15, |
|
|
|
color: isDarkMode |
|
|
|
? Colors.white |
|
|
|
: Colors.grey[800], |
|
|
|
), |
|
|
|
decoration: InputDecoration( |
|
|
|
hintText: isImageActive |
|
|
|
? '添加对图片的问题或描述...' |
|
|
|
: '输入消息...', |
|
|
|
hintStyle: TextStyle( |
|
|
|
color: Colors.grey[500], |
|
|
|
fontSize: 15, |
|
|
|
), |
|
|
|
filled: false, |
|
|
|
border: InputBorder.none, |
|
|
|
focusedBorder: OutlineInputBorder( |
|
|
|
borderSide: BorderSide( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[800]! |
|
|
|
: Colors.grey[200]!, |
|
|
|
width: 1.0, |
|
|
|
), |
|
|
|
), |
|
|
|
contentPadding: EdgeInsets.symmetric( |
|
|
|
vertical: isImageActive |
|
|
|
? 12 |
|
|
|
: 8), // 调整垂直内边距 |
|
|
|
), |
|
|
|
onSubmitted: (_) => |
|
|
|
controller.sendMessage(), |
|
|
|
); |
|
|
|
}), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
); |
|
|
|
} else { |
|
|
|
// 语音输入模式 - 始终显示波形图 |
|
|
|
// 输入控件区域 - 包含输入行和展开的选项 |
|
|
|
Column( |
|
|
|
children: [ |
|
|
|
// 输入控件行 - 使用IntrinsicHeight确保按钮与输入框底部对齐 |
|
|
|
IntrinsicHeight( |
|
|
|
child: Row( |
|
|
|
crossAxisAlignment: CrossAxisAlignment.end, // 所有元素底部对齐 |
|
|
|
children: [ |
|
|
|
// 键盘/语音切换按钮 |
|
|
|
Obx(() { |
|
|
|
final isTextMode = controller.isTextInputMode.value; |
|
|
|
return GestureDetector( |
|
|
|
onTap: () { |
|
|
|
if (controller.isListening.value) { |
|
|
|
controller.stopVoiceInput(); |
|
|
|
} else { |
|
|
|
controller.startVoiceInput(); |
|
|
|
} |
|
|
|
controller.toggleInputMode(); |
|
|
|
}, |
|
|
|
child: Container( |
|
|
|
height: 50, |
|
|
|
padding: |
|
|
|
const EdgeInsets.symmetric(horizontal: 16), |
|
|
|
width: 44, |
|
|
|
height: 44, |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[800] |
|
|
|
: Colors.grey[200], |
|
|
|
borderRadius: BorderRadius.circular(22), |
|
|
|
border: Border.all( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[800]! |
|
|
|
: Colors.grey.withOpacity(0.1), |
|
|
|
width: 1.0, |
|
|
|
), |
|
|
|
), |
|
|
|
child: Row( |
|
|
|
children: [ |
|
|
|
// 语音波形图 - 始终显示 |
|
|
|
Expanded( |
|
|
|
child: Row( |
|
|
|
mainAxisAlignment: |
|
|
|
MainAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
...List.generate(10, (index) { |
|
|
|
return _buildSoundBar(index); |
|
|
|
}), |
|
|
|
const Spacer(), |
|
|
|
// "正在聆听"文字 |
|
|
|
Obx(() { |
|
|
|
final isListening = |
|
|
|
controller.isListening.value; |
|
|
|
return Row( |
|
|
|
children: [ |
|
|
|
Icon( |
|
|
|
Icons.mic, |
|
|
|
size: 14, |
|
|
|
color: isListening |
|
|
|
? primaryColor |
|
|
|
: Colors.grey[500], |
|
|
|
), |
|
|
|
const SizedBox(width: 4), |
|
|
|
Text( |
|
|
|
'正在聆听', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 13, |
|
|
|
fontWeight: FontWeight.w500, |
|
|
|
color: isListening |
|
|
|
? primaryColor |
|
|
|
: Colors.grey[500], |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
); |
|
|
|
}), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
child: Icon( |
|
|
|
isTextMode ? Icons.mic_none : Icons.keyboard, |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[300] |
|
|
|
: Colors.grey[700], |
|
|
|
size: 22, |
|
|
|
), |
|
|
|
), |
|
|
|
); |
|
|
|
} |
|
|
|
}), |
|
|
|
), |
|
|
|
}), |
|
|
|
|
|
|
|
const SizedBox(width: 8), |
|
|
|
const SizedBox(width: 8), |
|
|
|
|
|
|
|
// 动态按钮区域:展开按钮 + 发送按钮 或 图片选择按钮 |
|
|
|
Obx(() { |
|
|
|
final hasText = controller.hasTextInput.value; |
|
|
|
final hasImage = controller.isImageInputActive.value; |
|
|
|
final shouldShowSend = hasText || hasImage; |
|
|
|
final shouldShowExpand = |
|
|
|
controller.shouldShowExpandButton.value; |
|
|
|
// 文本输入框或语音波形图 |
|
|
|
Expanded( |
|
|
|
child: Obx(() { |
|
|
|
final isTextMode = |
|
|
|
controller.isTextInputMode.value; |
|
|
|
|
|
|
|
if (shouldShowSend) { |
|
|
|
// 显示发送按钮(可能还有展开按钮) |
|
|
|
return Column( |
|
|
|
mainAxisSize: MainAxisSize.min, |
|
|
|
children: [ |
|
|
|
// 展开按钮(在发送按钮上方) |
|
|
|
if (shouldShowExpand && hasText) |
|
|
|
Padding( |
|
|
|
padding: const EdgeInsets.only(bottom: 40), |
|
|
|
child: GestureDetector( |
|
|
|
onTap: () { |
|
|
|
// 打开底部弹出编辑模式 |
|
|
|
controller.openFullScreenEdit(); |
|
|
|
showModalBottomSheet( |
|
|
|
context: context, |
|
|
|
isScrollControlled: true, |
|
|
|
backgroundColor: Colors.transparent, |
|
|
|
builder: (context) => |
|
|
|
const FullScreenEditor(), |
|
|
|
).then((_) { |
|
|
|
// 弹出页关闭时的回调 |
|
|
|
controller.closeFullScreenEdit(); |
|
|
|
}); |
|
|
|
}, |
|
|
|
child: Container( |
|
|
|
width: 32, |
|
|
|
height: 32, |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[700] |
|
|
|
: Colors.grey[300], |
|
|
|
borderRadius: BorderRadius.circular(16), |
|
|
|
), |
|
|
|
child: Icon( |
|
|
|
Icons.expand_less, |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[300] |
|
|
|
: Colors.grey[700], |
|
|
|
size: 20, |
|
|
|
), |
|
|
|
), |
|
|
|
if (isTextMode) { |
|
|
|
// 文本输入模式 - 限制最大高度并只向上扩展 |
|
|
|
return Container( |
|
|
|
constraints: const BoxConstraints( |
|
|
|
minHeight: 44, // 最小高度 |
|
|
|
maxHeight: 120, // 最大高度限制,防止挤压按钮 |
|
|
|
), |
|
|
|
), |
|
|
|
// 发送按钮 |
|
|
|
GestureDetector( |
|
|
|
onTap: () => controller.sendMessage(), |
|
|
|
child: Container( |
|
|
|
width: 44, |
|
|
|
height: 44, |
|
|
|
padding: const EdgeInsets.symmetric( |
|
|
|
horizontal: 16), |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: primaryColor, |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[800] |
|
|
|
: Colors.grey[200], |
|
|
|
borderRadius: BorderRadius.circular(22), |
|
|
|
border: Border.all( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[ |
|
|
|
800]! // 修改: 从 Colors.grey[700] 改为 Colors.grey[800] |
|
|
|
: Colors.grey.withOpacity(0.1), |
|
|
|
width: 1.0, |
|
|
|
), |
|
|
|
), |
|
|
|
child: const Icon( |
|
|
|
Icons.send, |
|
|
|
color: Colors.white, |
|
|
|
size: 22, |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
); |
|
|
|
} else { |
|
|
|
// 显示图片选择按钮 |
|
|
|
return Container( |
|
|
|
width: 44, |
|
|
|
height: 44, |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[800] |
|
|
|
: Colors.grey[200], |
|
|
|
borderRadius: BorderRadius.circular(22), |
|
|
|
), |
|
|
|
child: PopupMenuButton<String>( |
|
|
|
icon: Icon( |
|
|
|
Icons.add_photo_alternate_outlined, |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[300] |
|
|
|
: Colors.grey[700], |
|
|
|
size: 22, |
|
|
|
), |
|
|
|
padding: EdgeInsets.zero, |
|
|
|
shape: RoundedRectangleBorder( |
|
|
|
borderRadius: BorderRadius.circular(12), |
|
|
|
), |
|
|
|
onSelected: (value) async { |
|
|
|
if (value == 'camera') { |
|
|
|
await controller.takePhoto(); |
|
|
|
} else if (value == 'gallery') { |
|
|
|
await controller.pickImage(); |
|
|
|
} |
|
|
|
}, |
|
|
|
itemBuilder: (context) => [ |
|
|
|
PopupMenuItem<String>( |
|
|
|
value: 'camera', |
|
|
|
child: Row( |
|
|
|
crossAxisAlignment: |
|
|
|
CrossAxisAlignment.end, // 对齐到底部,实现向上扩展 |
|
|
|
children: [ |
|
|
|
Icon(Icons.camera_alt, |
|
|
|
color: primaryColor, size: 20), |
|
|
|
const SizedBox(width: 8), |
|
|
|
const Text('拍照', |
|
|
|
style: TextStyle(fontSize: 14)), |
|
|
|
Expanded( |
|
|
|
child: Obx(() { |
|
|
|
// 图片模式下使用更高的输入框 |
|
|
|
final isImageActive = controller |
|
|
|
.isImageInputActive.value; |
|
|
|
|
|
|
|
return TextField( |
|
|
|
controller: |
|
|
|
controller.textController, |
|
|
|
maxLines: isImageActive |
|
|
|
? 3 |
|
|
|
: 5, // 设置合理的最大行数 |
|
|
|
minLines: isImageActive ? 2 : 1, |
|
|
|
keyboardType: TextInputType |
|
|
|
.multiline, // 启用多行键盘 |
|
|
|
textInputAction: TextInputAction |
|
|
|
.newline, // 支持回车换行 |
|
|
|
onTap: () { |
|
|
|
// 当点击输入框时,如果图片选项已展开,则收缩 |
|
|
|
if (controller |
|
|
|
.isImageOptionsExpanded |
|
|
|
.value) { |
|
|
|
controller |
|
|
|
.collapseImageOptions(); |
|
|
|
} |
|
|
|
}, |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 15, |
|
|
|
color: isDarkMode |
|
|
|
? Colors.white |
|
|
|
: Colors.grey[800], |
|
|
|
), |
|
|
|
decoration: InputDecoration( |
|
|
|
hintText: isImageActive |
|
|
|
? '添加对图片的问题或描述...' |
|
|
|
: '输入消息...', |
|
|
|
hintStyle: TextStyle( |
|
|
|
color: Colors.grey[500], |
|
|
|
fontSize: 15, |
|
|
|
), |
|
|
|
filled: false, |
|
|
|
border: InputBorder.none, |
|
|
|
focusedBorder: OutlineInputBorder( |
|
|
|
borderSide: BorderSide( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[800]! |
|
|
|
: Colors.grey[200]!, |
|
|
|
width: 1.0, |
|
|
|
), |
|
|
|
), |
|
|
|
contentPadding: |
|
|
|
EdgeInsets.symmetric( |
|
|
|
vertical: isImageActive |
|
|
|
? 12 |
|
|
|
: 8), // 调整垂直内边距 |
|
|
|
), |
|
|
|
onSubmitted: (_) => |
|
|
|
controller.sendMessage(), |
|
|
|
); |
|
|
|
}), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
PopupMenuItem<String>( |
|
|
|
value: 'gallery', |
|
|
|
child: Row( |
|
|
|
children: [ |
|
|
|
Icon(Icons.photo_library, |
|
|
|
color: primaryColor, size: 20), |
|
|
|
const SizedBox(width: 8), |
|
|
|
const Text('从相册选择', |
|
|
|
style: TextStyle(fontSize: 14)), |
|
|
|
], |
|
|
|
); |
|
|
|
} else { |
|
|
|
// 语音输入模式 - 始终显示波形图 |
|
|
|
return GestureDetector( |
|
|
|
onTap: () { |
|
|
|
if (controller.isListening.value) { |
|
|
|
controller.stopVoiceInput(); |
|
|
|
} else { |
|
|
|
controller.startVoiceInput(); |
|
|
|
} |
|
|
|
}, |
|
|
|
child: Container( |
|
|
|
height: 50, |
|
|
|
padding: const EdgeInsets.symmetric( |
|
|
|
horizontal: 16), |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[800] |
|
|
|
: Colors.grey[200], |
|
|
|
borderRadius: BorderRadius.circular(22), |
|
|
|
border: Border.all( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[800]! |
|
|
|
: Colors.grey.withOpacity(0.1), |
|
|
|
width: 1.0, |
|
|
|
), |
|
|
|
), |
|
|
|
child: Row( |
|
|
|
children: [ |
|
|
|
// 语音波形图 - 始终显示 |
|
|
|
Expanded( |
|
|
|
child: Row( |
|
|
|
mainAxisAlignment: |
|
|
|
MainAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
...List.generate(10, (index) { |
|
|
|
return _buildSoundBar(index); |
|
|
|
}), |
|
|
|
const Spacer(), |
|
|
|
// "正在聆听"文字 |
|
|
|
Obx(() { |
|
|
|
final isListening = controller |
|
|
|
.isListening.value; |
|
|
|
return Row( |
|
|
|
children: [ |
|
|
|
Icon( |
|
|
|
Icons.mic, |
|
|
|
size: 14, |
|
|
|
color: isListening |
|
|
|
? primaryColor |
|
|
|
: Colors.grey[500], |
|
|
|
), |
|
|
|
const SizedBox(width: 4), |
|
|
|
Text( |
|
|
|
'正在聆听', |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 13, |
|
|
|
fontWeight: |
|
|
|
FontWeight.w500, |
|
|
|
color: isListening |
|
|
|
? primaryColor |
|
|
|
: Colors.grey[500], |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
); |
|
|
|
}), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
); |
|
|
|
} |
|
|
|
}), |
|
|
|
), |
|
|
|
|
|
|
|
const SizedBox(width: 8), |
|
|
|
|
|
|
|
// 动态按钮区域:展开按钮 + 发送按钮 或 图片选择按钮 |
|
|
|
Obx(() { |
|
|
|
final hasText = controller.hasTextInput.value; |
|
|
|
final hasImage = |
|
|
|
controller.isImageInputActive.value; |
|
|
|
final shouldShowSend = hasText || hasImage; |
|
|
|
final shouldShowExpand = |
|
|
|
controller.shouldShowExpandButton.value; |
|
|
|
|
|
|
|
if (shouldShowSend) { |
|
|
|
// 显示发送按钮(可能还有展开按钮) |
|
|
|
return Column( |
|
|
|
mainAxisSize: MainAxisSize.min, |
|
|
|
children: [ |
|
|
|
// 展开按钮(在发送按钮上方) |
|
|
|
if (shouldShowExpand && hasText) |
|
|
|
Padding( |
|
|
|
padding: |
|
|
|
const EdgeInsets.only(bottom: 40), |
|
|
|
child: GestureDetector( |
|
|
|
onTap: () { |
|
|
|
// 打开底部弹出编辑模式 |
|
|
|
controller.openFullScreenEdit(); |
|
|
|
showModalBottomSheet( |
|
|
|
context: context, |
|
|
|
isScrollControlled: true, |
|
|
|
backgroundColor: Colors.transparent, |
|
|
|
builder: (context) => |
|
|
|
const FullScreenEditor(), |
|
|
|
).then((_) { |
|
|
|
// 弹出页关闭时的回调 |
|
|
|
controller.closeFullScreenEdit(); |
|
|
|
}); |
|
|
|
}, |
|
|
|
child: Container( |
|
|
|
width: 32, |
|
|
|
height: 32, |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[700] |
|
|
|
: Colors.grey[300], |
|
|
|
borderRadius: |
|
|
|
BorderRadius.circular(16), |
|
|
|
), |
|
|
|
child: Icon( |
|
|
|
Icons.expand_less, |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[300] |
|
|
|
: Colors.grey[700], |
|
|
|
size: 20, |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
// 发送按钮 |
|
|
|
GestureDetector( |
|
|
|
onTap: () => controller.sendMessage(), |
|
|
|
child: Container( |
|
|
|
width: 44, |
|
|
|
height: 44, |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: primaryColor, |
|
|
|
borderRadius: BorderRadius.circular(22), |
|
|
|
), |
|
|
|
child: const Icon( |
|
|
|
Icons.send, |
|
|
|
color: Colors.white, |
|
|
|
size: 22, |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
); |
|
|
|
} else { |
|
|
|
// 显示'+'选择按钮 |
|
|
|
return Obx(() { |
|
|
|
final isExpanded = |
|
|
|
controller.isImageOptionsExpanded.value; |
|
|
|
return GestureDetector( |
|
|
|
onTap: () { |
|
|
|
controller.toggleImageOptions(); |
|
|
|
}, |
|
|
|
child: Container( |
|
|
|
width: 44, |
|
|
|
height: 44, |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[800] |
|
|
|
: Colors.grey[200], |
|
|
|
borderRadius: BorderRadius.circular(22), |
|
|
|
), |
|
|
|
child: Icon( |
|
|
|
isExpanded ? Icons.close : Icons.add, |
|
|
|
color: isDarkMode |
|
|
|
? Colors.grey[300] |
|
|
|
: Colors.grey[700], |
|
|
|
size: 22, |
|
|
|
), |
|
|
|
), |
|
|
|
); |
|
|
|
}); |
|
|
|
} |
|
|
|
}), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
// 展开的'+'选择选项 |
|
|
|
Obx(() { |
|
|
|
final isExpanded = |
|
|
|
controller.isImageOptionsExpanded.value; |
|
|
|
return AnimatedContainer( |
|
|
|
duration: const Duration(milliseconds: 300), |
|
|
|
curve: Curves.easeInOut, |
|
|
|
height: isExpanded ? 100.h : 0, // 调整高度以适应按钮尺寸 |
|
|
|
child: isExpanded |
|
|
|
? Container( |
|
|
|
margin: EdgeInsets.only(top: 8.h), |
|
|
|
padding: EdgeInsets.symmetric(horizontal:16.w), // 添加左右边距 |
|
|
|
child: Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly, // 改为均匀分布 |
|
|
|
children: [ |
|
|
|
// 相机按钮 |
|
|
|
_buildPlusOptionButton( |
|
|
|
context: context, |
|
|
|
icon: Icons.camera_alt, |
|
|
|
label: '拍照', |
|
|
|
onTap: () async { |
|
|
|
//controller.collapseImageOptions(); |
|
|
|
await controller.takePhoto(); |
|
|
|
}, |
|
|
|
isDarkMode: isDarkMode, |
|
|
|
), |
|
|
|
// 相册按钮 |
|
|
|
_buildPlusOptionButton( |
|
|
|
context: context, |
|
|
|
icon: Icons.photo_library, |
|
|
|
label: '相册', |
|
|
|
onTap: () async { |
|
|
|
//controller.collapseImageOptions(); |
|
|
|
await controller.pickImage(); |
|
|
|
}, |
|
|
|
isDarkMode: isDarkMode, |
|
|
|
), |
|
|
|
// 文件按钮(预留位置) |
|
|
|
// _buildPlusOptionButton( |
|
|
|
// context: context, |
|
|
|
// icon: Icons.folder, |
|
|
|
// label: '文件', |
|
|
|
// onTap: () { |
|
|
|
// //controller.collapseImageOptions(); |
|
|
|
// // TODO: 实现文件选择功能 |
|
|
|
// }, |
|
|
|
// isDarkMode: isDarkMode, |
|
|
|
// ), |
|
|
|
// 通话按钮(预留位置) |
|
|
|
_buildPlusOptionButton( |
|
|
|
context: context, |
|
|
|
icon: Icons.phone, |
|
|
|
label: '打电话', |
|
|
|
onTap: () { |
|
|
|
//controller.collapseImageOptions(); |
|
|
|
// TODO: 实现通话功能 |
|
|
|
}, |
|
|
|
isDarkMode: isDarkMode, |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
); |
|
|
|
} |
|
|
|
}), |
|
|
|
], |
|
|
|
), |
|
|
|
) |
|
|
|
: const SizedBox.shrink(), |
|
|
|
); |
|
|
|
}), |
|
|
|
], |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
@ -792,6 +883,62 @@ class AgentView extends StatelessWidget { |
|
|
|
)), |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
// 构建"+"号展开选项按钮 |
|
|
|
Widget _buildPlusOptionButton({ |
|
|
|
required BuildContext context, |
|
|
|
required IconData icon, |
|
|
|
required String label, |
|
|
|
required VoidCallback onTap, |
|
|
|
required bool isDarkMode, |
|
|
|
}) { |
|
|
|
final primaryColor = Theme.of(context).primaryColor; |
|
|
|
|
|
|
|
return GestureDetector( |
|
|
|
onTap: onTap, |
|
|
|
child: SizedBox( |
|
|
|
// 调整为一行四个按钮的宽度,考虑屏幕宽度和间距 |
|
|
|
width: (1.sw - 100.w) / 4, // 屏幕宽度减去左右边距,除以4个按钮 |
|
|
|
child: Column( |
|
|
|
mainAxisSize: MainAxisSize.min, |
|
|
|
mainAxisAlignment: MainAxisAlignment.center, |
|
|
|
children: [ |
|
|
|
// 正方形图标容器 - 增大尺寸 |
|
|
|
Container( |
|
|
|
width: 50.w, // 调整为44.w以适应布局 |
|
|
|
height: 50.w, // 调整为44.w以适应布局 |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: isDarkMode ? Colors.grey[800] : Colors.grey[100], |
|
|
|
borderRadius: BorderRadius.circular(12.r), // 调整为10.r |
|
|
|
border: Border.all( |
|
|
|
color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!, |
|
|
|
width: 1, |
|
|
|
), |
|
|
|
), |
|
|
|
child: Icon( |
|
|
|
icon, |
|
|
|
color: primaryColor, |
|
|
|
size: 20.sp, // 调整为18.sp |
|
|
|
), |
|
|
|
), |
|
|
|
SizedBox(height: 4.h), // 调整为3.h |
|
|
|
// 文字标签 |
|
|
|
Text( |
|
|
|
label, |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 10.sp, // 调整为9.sp |
|
|
|
color: isDarkMode ? Colors.grey[300] : Colors.grey[700], |
|
|
|
fontWeight: FontWeight.w500, |
|
|
|
), |
|
|
|
textAlign: TextAlign.center, |
|
|
|
maxLines: 1, |
|
|
|
overflow: TextOverflow.ellipsis, |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class _ReportForm extends StatelessWidget { |
|
|
|
|