wolfplus 2 years ago
parent
commit
a1c3903524
  1. 19
      lib/modules/chat/controllers/chat_controller.dart
  2. 32
      lib/modules/translation/controllers/translation_controller.dart
  3. 136
      lib/modules/translation/views/translation_view.dart

19
lib/modules/chat/controllers/chat_controller.dart

@ -124,9 +124,6 @@ class ChatController extends GetxController {
// 使用微任务在布局完成后自动滚动到底部
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToBottom();
Future.delayed(const Duration(milliseconds: 500), () {
_scrollToBottom();
});
});
}
@ -227,7 +224,7 @@ class ChatController extends GetxController {
if (scrollController.hasClients && messages.isNotEmpty) {
try {
scrollController.animateTo(
scrollController.position.maxScrollExtent + 30,
scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 300),
curve: Curves.easeOut,
);
@ -280,7 +277,7 @@ class ChatController extends GetxController {
timestamp: DateTime.now(),
);
messages.add(userMessage);
_scrollToBottom();
// 清空输入框
inputText.value = '';
@ -288,7 +285,10 @@ class ChatController extends GetxController {
// 添加加载中的消息
final loadingMessage = Message.loading();
messages.add(loadingMessage);
_scrollToBottom();
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToBottom();
});
isSending.value = true;
@ -549,13 +549,8 @@ class ChatController extends GetxController {
messages.add(tempMessage);
_tempMessageIndex = messages.length - 1;
messages.refresh();
_scrollToBottom();
}
// 取消之前的计时器
_scrollDebounceTimer?.cancel();
// 设置新的防抖计时器,延迟100毫秒执行滚动
_scrollDebounceTimer = Timer(Duration(milliseconds: 500), () {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToBottom();
});
}

32
lib/modules/translation/controllers/translation_controller.dart

@ -90,11 +90,7 @@ class TranslationController extends GetxController {
// 使用微任务在布局完成后自动滚动到底部
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToBottom();
// 额外的延迟滚动,确保在复杂视图中也能正确滚动
Future.delayed(const Duration(milliseconds: 500), () {
_scrollToBottom();
});
});
}
@ -179,20 +175,20 @@ class TranslationController extends GetxController {
currentSessionId = DateTime.now().millisecondsSinceEpoch.toString();
// 初始化新的翻译项
final newItem = TranslationItem(
sourceText: '',
translatedText: '',
sourceLanguageCode: sourceLanguageCode.value,
targetLanguageCode: targetLanguageCode.value,
timestamp: DateTime.now(),
sessionId: currentSessionId!,
isFirstInSession: true,
isIntermediate: true,
);
// // 初始化新的翻译项
// final newItem = TranslationItem(
// sourceText: '',
// translatedText: '',
// sourceLanguageCode: sourceLanguageCode.value,
// targetLanguageCode: targetLanguageCode.value,
// timestamp: DateTime.now(),
// sessionId: currentSessionId!,
// isFirstInSession: true,
// isIntermediate: true,
// );
// 添加到历史记录
translationHistory.add(newItem);
// // 添加到历史记录
// translationHistory.add(newItem);
// 开始连续语音识别
final recognitionStream = await _asrService.startContinuousRecognition();

136
lib/modules/translation/views/translation_view.dart

@ -157,16 +157,14 @@ class TranslationView extends GetView<TranslationController> {
return Obx(() {
final itemCount = controller.translationHistory.length;
// 计算底部内边距,确保内容不会被底部框遮住
final bottomPadding = controller.bottomBarHeight + 20.h; // 使用控制栏高度加上额外间距
return ListView.builder(
controller: controller.scrollController,
padding: EdgeInsets.only(
left: 16.w,
right: 16.w,
top: 16.h,
bottom: bottomPadding
bottom: 16.h,
),
itemCount: itemCount,
itemBuilder: (context, index) {
@ -175,97 +173,77 @@ class TranslationView extends GetView<TranslationController> {
// 构建项目widget
return _buildTranslationItemWidget(
historyItem.sourceText,
historyItem.translatedText,
historyItem.isIntermediate, // 如果是中间结果,设置为当前项
item: historyItem,
isIntermediate: historyItem.isIntermediate,
isDarkMode: isDarkMode,
context,
historyItem
);
},
);
});
}
Widget _buildTranslationItemWidget(
String sourceText,
String translatedText,
bool isCurrentItem,
{
TranslationItem? item,
bool isIntermediate = false,
required bool isDarkMode,
}
) {
// 使用AnimatedContainer实现平滑过渡,但恢复原来的视觉样式
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.only(bottom: 24.h),
Widget _buildTranslationItemWidget(BuildContext context, TranslationItem item) {
return Container(
margin: const EdgeInsets.only(bottom: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDarkMode ? AppColors.cardBackground : Colors.grey[100],
borderRadius: BorderRadius.circular(12.r),
color: item.isIntermediate
? Colors.grey.withOpacity(0.1) // 中间结果使用浅灰色底色
: (item.sourceLanguageCode == controller.sourceLanguageCode.value
? Theme.of(context).colorScheme.primary.withOpacity(0.08) // 源语言匹配时使用主题色底色
: Theme.of(context).colorScheme.secondary.withOpacity(0.08)), // 否则使用次要色底色
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: item.isIntermediate
? Colors.grey.withOpacity(0.2)
: Colors.transparent,
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Source text
AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: double.infinity,
padding: EdgeInsets.all(16.r),
child: Text(
sourceText.isEmpty && isCurrentItem ? '等待语音输入...' : sourceText,
// 源文本
Text(
item.sourceText,
style: TextStyle(
fontSize: 16,
color: Theme.of(context).textTheme.bodyLarge?.color,
),
),
if (item.translatedText.isNotEmpty) ...[
const SizedBox(height: 8),
// 翻译文本
Text(
item.translatedText,
style: TextStyle(
fontSize: 16.sp,
color: sourceText.isEmpty && isCurrentItem
? (isDarkMode ? Colors.grey[400] : Colors.grey)
: (isDarkMode ? Colors.white : Colors.black),
fontWeight: FontWeight.normal,
fontSize: 16,
color: Theme.of(context).textTheme.bodyMedium?.color,
fontWeight: FontWeight.w500,
),
),
),
// Divider
Divider(height: 1, thickness: 1, color: isDarkMode ? Colors.grey[800] : Colors.grey[200]),
// Translated text
AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: double.infinity,
padding: EdgeInsets.all(16.r),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
translatedText.isEmpty && isCurrentItem ? '翻译结果将显示在这里...' : translatedText,
style: TextStyle(
fontSize: 16.sp,
color: translatedText.isEmpty && isCurrentItem
? (isDarkMode ? Colors.grey[400] : Colors.grey)
: (isDarkMode ? Colors.white : Colors.black),
fontStyle: FontStyle.normal,
),
),
],
// 语言信息和时间戳
const SizedBox(height: 4),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${item.sourceLanguageName} → ${item.targetLanguageName}',
style: TextStyle(
fontSize: 12,
color: Theme.of(context).textTheme.bodySmall?.color,
),
if (translatedText.isNotEmpty)
IconButton(
icon: Icon(
Icons.volume_up,
color: isDarkMode ? AppColors.primary : Colors.blue,
size: 20.sp,
),
onPressed: () {
if (item != null) {
controller.playTranslationItem(item);
} else {
controller.playTranslatedText(translatedText);
}
},
),
if (!item.isIntermediate)
GestureDetector(
onTap: () => controller.playTranslationItem(item),
child: Icon(
Icons.volume_up,
size: 18,
color: Theme.of(context).colorScheme.primary,
),
],
),
),
],
),
],
),

Loading…
Cancel
Save