Browse Source

refactor(agent): 优化消息气泡布局和样式

- 调整消息气泡的内边距和圆角样式,提升视觉一致性
- 移除冗余的播放列表容器和按钮,简化UI结构
- 修复变量命名错误,将`mate`改为`meta`
weicu
lxm 1 year ago
parent
commit
4ed2d05d73
  1. 6
      .gitignore
  2. 12
      lib/modules/agent/views/agent_view.dart
  3. 205
      lib/modules/agent/views/message_bubble.dart
  4. 3
      lib/modules/home/controllers/home_controller.dart

6
.gitignore

@ -136,3 +136,9 @@ coverage/
*.js_ *.js_
*.js.deps *.js.deps
*.js.map *.js.map
linux/flutter/generated_plugin_registrant.cc
linux/flutter/generated_plugin_registrant.h
linux/flutter/generated_plugins.cmake
windows/flutter/generated_plugin_registrant.cc
windows/flutter/generated_plugin_registrant.h
windows/flutter/generated_plugins.cmake

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

@ -149,8 +149,11 @@ class AgentView extends StatelessWidget {
// 聊天内容 // 聊天内容
Expanded( Expanded(
child: Obx( child: Obx(
() => ListView.builder( () => Align(
alignment: Alignment.topCenter,
child: ListView.builder(
controller: controller.scrollController, controller: controller.scrollController,
shrinkWrap: true,
reverse: true, reverse: true,
padding: padding:
const EdgeInsets.symmetric(vertical: 16, horizontal: 16), const EdgeInsets.symmetric(vertical: 16, horizontal: 16),
@ -167,13 +170,14 @@ class AgentView extends StatelessWidget {
isRecognizing: message.isRecognizing, isRecognizing: message.isRecognizing,
hasImage: message.hasImage, hasImage: message.hasImage,
imagePath: message.imagePath, imagePath: message.imagePath,
mate: message.meta, meta: message.meta,
), ),
); );
}, },
), ),
), ),
), ),
),
// 输入区域 // 输入区域
Container( Container(
@ -308,7 +312,7 @@ class AgentView extends StatelessWidget {
if (isTextMode) { if (isTextMode) {
// 文本输入模式 - 注意观察图片模式时的高度变化 // 文本输入模式 - 注意观察图片模式时的高度变化
return Container( return Container(
height: 44, //height: 44,
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.grey[200], color: Colors.grey[200],
@ -378,7 +382,7 @@ class AgentView extends StatelessWidget {
} }
}, },
child: Container( child: Container(
height: 44, height: 50,
padding: padding:
const EdgeInsets.symmetric(horizontal: 16), const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration( decoration: BoxDecoration(

205
lib/modules/agent/views/message_bubble.dart

@ -15,7 +15,7 @@ class MessageBubble extends GetView<AgentController> {
final bool isRecognizing; final bool isRecognizing;
final bool hasImage; final bool hasImage;
final String? imagePath; final String? imagePath;
final Map? mate; final Map? meta;
const MessageBubble({ const MessageBubble({
Key? key, Key? key,
@ -25,7 +25,7 @@ class MessageBubble extends GetView<AgentController> {
this.isRecognizing = false, this.isRecognizing = false,
this.hasImage = false, this.hasImage = false,
this.imagePath, this.imagePath,
this.mate, this.meta,
}) : super(key: key); }) : super(key: key);
@override @override
@ -39,8 +39,8 @@ class MessageBubble extends GetView<AgentController> {
isUser ? MainAxisAlignment.end : MainAxisAlignment.start, isUser ? MainAxisAlignment.end : MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (!isUser) _buildAvatar(isUser, context), //if (!isUser) _buildAvatar(isUser, context),
const SizedBox(width: 8), //const SizedBox(width: 8),
Flexible( Flexible(
child: Column( child: Column(
crossAxisAlignment: crossAxisAlignment:
@ -50,15 +50,23 @@ class MessageBubble extends GetView<AgentController> {
padding: hasImage padding: hasImage
? (message == '[图片]' ? (message == '[图片]'
? EdgeInsets.zero ? EdgeInsets.zero
: const EdgeInsets.all(12)) : isUser
: const EdgeInsets.all(12), ? const EdgeInsets.fromLTRB(
12, 12, 16, 12) // 用户消息右侧多一点内边距
: const EdgeInsets.fromLTRB(
16, 12, 12, 12)) // AI消息左侧多一点内边距
: isUser
? const EdgeInsets.fromLTRB(
12, 12, 16, 12) // 用户消息右侧多一点内边距
: const EdgeInsets.fromLTRB(
16, 12, 12, 12), // AI消息左侧多一点内边距
decoration: BoxDecoration( decoration: BoxDecoration(
color: _getBubbleColor(context), color: _getBubbleColor(context),
borderRadius: BorderRadius.circular(18).copyWith( borderRadius: BorderRadius.circular(18).copyWith(
bottomLeft: isUser topLeft: isUser
? const Radius.circular(18) ? const Radius.circular(18)
: const Radius.circular(4), : const Radius.circular(4),
bottomRight: !isUser topRight: !isUser
? const Radius.circular(18) ? const Radius.circular(18)
: const Radius.circular(4), : const Radius.circular(4),
), ),
@ -86,8 +94,8 @@ class MessageBubble extends GetView<AgentController> {
], ],
), ),
), ),
const SizedBox(width: 8), //const SizedBox(width: 8),
if (isUser) _buildAvatar(isUser, context), //if (isUser) _buildAvatar(isUser, context),
], ],
), ),
); );
@ -231,28 +239,29 @@ class MessageBubble extends GetView<AgentController> {
} }
// 判断消息内容,显示特殊卡片 // 判断消息内容,显示特殊卡片
if (!isUser && mate != null) { if (!isUser && meta != null) {
if (mate!.keys.contains('card_music')) { // 判断消息类型,显示不同的卡片
final musicId = mate!['card_music']['id'].toString(); if (meta!.keys.contains('card_music')) {
final musicName = mate!['card_music']['name'] as String; final musicId = meta!['card_music']['id'].toString();
final musicUrl = mate!['card_music']['url'] as String; final musicName = meta!['card_music']['name'] as String;
final musicSinger = mate!['card_music']['sgener'] as String; final musicUrl = meta!['card_music']['url'] as String;
final musicImage = mate!['card_music']['image'] as String; final musicSinger = meta!['card_music']['sgener'] as String;
final musicImage = meta!['card_music']['image'] as String;
//音乐卡片 //音乐卡片
return _buildMusicItem( return _buildMusicItem(
context, musicId, musicName, musicUrl, musicSinger, musicImage); context, musicId, musicName, musicUrl, musicSinger, musicImage);
} else if (mate!.keys.contains('card_musiclist')) { } else if (meta!.keys.contains('card_musiclist')) {
final rawList = mate!['card_musiclist']['musics'] as List; final rawList = meta!['card_musiclist']['musics'] as List;
final playlist = final playlist =
rawList.map((e) => Map<String, dynamic>.from(e)).toList(); rawList.map((e) => Map<String, dynamic>.from(e)).toList();
//音乐播放列表 //音乐播放列表
return _buildMusicContent(context, playlist); return _buildMusicContent(context, playlist);
} else if (mate!.keys.contains('card_weather')) { } else if (meta!.keys.contains('card_weather')) {
final city = mate!['card_weather']['city'] as String; final city = meta!['card_weather']['city'] as String;
final rawList = mate!['card_weather']['dayweather'] as List; final rawList = meta!['card_weather']['dayweather'] as List;
final daylist = final daylist =
rawList.map((e) => Map<String, dynamic>.from(e)).toList(); rawList.map((e) => Map<String, dynamic>.from(e)).toList();
final now = mate!['card_weather']['day'] as Map<String, dynamic>; final now = meta!['card_weather']['day'] as Map<String, dynamic>;
//天气 //天气
return _buildWeatherContent( return _buildWeatherContent(
context, context,
@ -321,21 +330,22 @@ class MessageBubble extends GetView<AgentController> {
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Container( // Container(
padding: const EdgeInsets.all(12), // padding: const EdgeInsets.all(12),
decoration: BoxDecoration( // decoration: BoxDecoration(
color: Colors.white, // color: Colors.white,
borderRadius: BorderRadius.circular(12), // borderRadius: BorderRadius.circular(12),
boxShadow: [ // boxShadow: [
BoxShadow( // BoxShadow(
color: Colors.black.withOpacity(0.03), // color: Colors.black.withOpacity(0.03),
blurRadius: 3, // blurRadius: 3,
spreadRadius: 1, // spreadRadius: 1,
offset: const Offset(0, 1), // offset: const Offset(0, 1),
), // ),
], // ],
), // ),
child: Column( // child:
Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
@ -359,7 +369,7 @@ class MessageBubble extends GetView<AgentController> {
const SizedBox(height: 10), const SizedBox(height: 10),
// 动态生成音乐条目 // 动态生成音乐条目
...playlist.asMap().entries.map((entry) { ...playlist.asMap().entries.take(4).map((entry) {
final index = entry.key; final index = entry.key;
final item = entry.value; final item = entry.value;
final id = item["id"]?.toString() ?? ""; final id = item["id"]?.toString() ?? "";
@ -396,47 +406,47 @@ class MessageBubble extends GetView<AgentController> {
), ),
], ],
), ),
), //),
const SizedBox(height: 10), //const SizedBox(height: 10),
Row( // Row(
mainAxisSize: MainAxisSize.min, // mainAxisSize: MainAxisSize.min,
children: [ // children: [
Expanded( // Expanded(
child: OutlinedButton.icon( // child: OutlinedButton.icon(
onPressed: () {}, // onPressed: () {},
icon: Icon(Icons.play_arrow, size: 16), // icon: Icon(Icons.play_arrow, size: 16),
label: Text('播放全部'), // label: Text('播放全部'),
style: OutlinedButton.styleFrom( // style: OutlinedButton.styleFrom(
foregroundColor: Theme.of(context).primaryColor, // foregroundColor: Theme.of(context).primaryColor,
side: BorderSide( // side: BorderSide(
color: Theme.of(context).primaryColor.withOpacity(0.5)), // color: Theme.of(context).primaryColor.withOpacity(0.5)),
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), // padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
shape: RoundedRectangleBorder( // shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16), // borderRadius: BorderRadius.circular(16),
), // ),
textStyle: TextStyle(fontSize: 12), // textStyle: TextStyle(fontSize: 12),
), // ),
), // ),
), // ),
SizedBox(width: 8), // SizedBox(width: 8),
Expanded( // Expanded(
child: OutlinedButton.icon( // child: OutlinedButton.icon(
onPressed: () {}, // onPressed: () {},
icon: Icon(Icons.shuffle, size: 16), // icon: Icon(Icons.shuffle, size: 16),
label: Text('随机播放'), // label: Text('随机播放'),
style: OutlinedButton.styleFrom( // style: OutlinedButton.styleFrom(
foregroundColor: Colors.grey[700], // foregroundColor: Colors.grey[700],
side: BorderSide(color: Colors.grey[400]!), // side: BorderSide(color: Colors.grey[400]!),
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), // padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
shape: RoundedRectangleBorder( // shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16), // borderRadius: BorderRadius.circular(16),
), // ),
textStyle: TextStyle(fontSize: 12), // textStyle: TextStyle(fontSize: 12),
), // ),
), // ),
), // ),
], // ],
), // ),
], ],
); );
} }
@ -578,22 +588,44 @@ class MessageBubble extends GetView<AgentController> {
'coverUrl': image, 'coverUrl': image,
}; };
controller.musicStatus(); //刷新状态 controller.musicStatus(); //刷新状态
return Padding( return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.03),
blurRadius: 3,
spreadRadius: 1,
offset: const Offset(0, 1),
),
],
),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8), padding: const EdgeInsets.symmetric(vertical: 8),
child: Row( child: Row(
children: [ children: [
Container( Container(
width: 38, width: 50,
height: 38, height: 50,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.grey[100], color: Colors.grey[100],
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
image: image.isNotEmpty
? DecorationImage(
image: NetworkImage(image),
fit: BoxFit.cover,
)
: null,
), ),
child: Icon( child: image.isEmpty
? Icon(
Icons.music_note, Icons.music_note,
size: 18, size: 18,
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
), )
: null,
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded( Expanded(
@ -647,11 +679,12 @@ class MessageBubble extends GetView<AgentController> {
), ),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
constraints: BoxConstraints(), constraints: BoxConstraints(),
iconSize: 24, iconSize: 36,
); );
}), }),
], ],
), ),
),
); );
} }

3
lib/modules/home/controllers/home_controller.dart

@ -103,6 +103,9 @@ class HomeController extends GetxController {
// 设置监听器监听蓝牙服务状态变化 // 设置监听器监听蓝牙服务状态变化
_setupStateListeners(); _setupStateListeners();
// 在初始化时自动触发一次刷新设备连接状态
refreshDeviceConnection();
} }
/// 设置状态监听器 /// 设置状态监听器

Loading…
Cancel
Save