flash-im-ui-style
闪讯 IM 的 UI 风格与样式规范。在编写 Flutter 页面时激活,确保视觉一致性。
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
闪讯 IM 的 UI 风格与样式规范。在编写 Flutter 页面时激活,确保视觉一致性。
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
GitHub Release 发布操作规范。在需要发布版本、上传安装包到 GitHub Release 时激活,确保使用正确的命令和流程。
确认/删除等交互弹框统一使用 showTolyPopPicker 底部弹出样式。在需要弹出确认框、删除确认、操作选择时激活,确保交互风格一致。
后端服务启动与数据库操作规范。在需要启动后端服务、运行 API 测试、执行数据库迁移或遇到连接错误时激活,确保使用正确的命令和流程。
功能归档规范。在归档功能版本、更新功能网、创建存档快照时激活,确保节点编号正确、网络图完整。
使用 tolyui_mediax 实现媒体预览。适用于图片九宫格展示、全屏预览、手势缩放、视频播放、Hero 动画等场景。
Flutter Widget/Page 组件代码评审技能。在需要审查组件代码质量、发现设计问题时激活,确保输出结构化的问题清单和改进建议。
| name | flash-im-ui-style |
| description | 闪讯 IM 的 UI 风格与样式规范。在编写 Flutter 页面时激活,确保视觉一致性。 |
| metadata | {"model":"manual","last_modified":"Sat, 19 Apr 2026 00:00:00 GMT"} |
| 用途 | 色值 | 说明 |
|---|---|---|
| 主色 | #3B82F6 | 蓝色,按钮/链接/选中态 |
| 微信绿 | #07C160 | Switch 激活色、在线状态 |
| 橙色 | #FF9800 | 警告/需验证按钮 |
| 红色 | #F44336 | 删除/退出/错误 |
| 文字主色 | #333333 | 标题、正文 |
| 文字次色 | #999999 | 副标题、描述、时间 |
| 文字提示色 | #BBBBBB | placeholder、hint |
| 分割线 | #F0F0F0 | 列表分割线(0.5px) |
| 页面背景 | #F5F5F5 | Scaffold 背景 |
| AppBar 背景 | #EDEDED | 灰色 AppBar(消息/通讯录 Tab) |
| AppBar 背景(白) | #FFFFFF | 白色 AppBar(详情页/设置页) |
| 卡片背景 | #FFFFFF | 列表项、设置项 |
| 输入框背景 | #F8F8F8 | TextField 填充色 |
两种风格:
灰色 AppBar(Tab 页面、搜索页):
AppBar(
title: const Text('标题'),
backgroundColor: const Color(0xFFEDEDED),
elevation: 0,
scrolledUnderElevation: 0,
)
白色 AppBar(详情页、设置页):
AppBar(
title: const Text('标题'),
backgroundColor: Colors.white,
foregroundColor: const Color(0xFF333333),
elevation: 0,
scrolledUnderElevation: 0,
)
Scaffold(backgroundColor: const Color(0xFFF5F5F5))
白色内容区域用 Container(color: Colors.white) 包裹,区域之间用 SizedBox(height: 10) 灰色间隔分组。
标准列表项(好友列表、申请列表):
EdgeInsets.symmetric(horizontal: 16, vertical: 12)Padding(left: 68) + Divider(height: 0.5, thickness: 0.5, color: Color(0xFFF0F0F0))设置项(群详情、聊天设置):
Container(
color: Colors.white,
child: ListTile(
title: Text(title, style: TextStyle(fontSize: 16, color: Color(0xFF333333))),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(value, style: TextStyle(fontSize: 14, color: Color(0xFF999999))),
SizedBox(width: 4),
Icon(Icons.chevron_right, color: Color(0xFFCCCCCC), size: 20),
],
),
),
)
设置项之间的分割线:
Container(
color: Colors.white,
padding: const EdgeInsets.only(left: 16),
child: const Divider(height: 0.5, thickness: 0.5),
)
操作按钮(好友申请/群通知的同意/拒绝):
拒绝(灰色文字):
GestureDetector(
onTap: onReject,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
child: Text('拒绝', style: TextStyle(fontSize: 13, color: Color(0xFF999999))),
),
)
同意/接受(蓝色填充):
GestureDetector(
onTap: onAccept,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 6),
decoration: BoxDecoration(
color: Color(0xFF3B82F6),
borderRadius: BorderRadius.circular(4),
),
child: Text('同意', style: TextStyle(fontSize: 13, color: Colors.white)),
),
)
搜索结果按钮(四种状态):
| 状态 | 样式 |
|---|---|
| 已加入/已申请 | 灰色背景 #F0F0F0 + 灰色文字 #999999 |
| 加入 | 蓝色填充 #3B82F6 + 白色文字 |
| 申请 | 橙色边框 #FF9800 + 橙色文字 |
按钮 padding: EdgeInsets.symmetric(horizontal: 10, vertical: 4),borderRadius: 4
[toly+] iOS 风格 CupertinoSwitch,主题蓝激活色:
SizedBox(
width: 56,
height: 32,
child: FittedBox(
fit: BoxFit.contain,
child: CupertinoSwitch(
value: value,
onChanged: onChanged,
activeTrackColor: const Color(0xFF3B82F6),
),
),
)
需要 import 'package:flutter/cupertino.dart';
通用头像(AvatarWidget):
identicon:seed 格式(像素风头像)http://... 网络图片群头像(GroupAvatarWidget):
grid:url1,url2,... 格式// 群主角标
Positioned(
right: -2, bottom: -2,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 3, vertical: 1),
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.circular(4),
),
child: Text('群主', style: TextStyle(fontSize: 8, color: Colors.white, fontWeight: FontWeight.bold)),
),
)
占位模式(点击跳转搜索页):
FlashSearchBar(hintText: '搜索', onTap: () => ...)
输入模式(可编辑):
FlashSearchBar(hintText: '搜索', editable: true, controller: ..., onChanged: ...)
[toly+] 自定义搜索框(带右侧 loading 指示器):
#EDEDED,输入框白色圆角 6px通讯录顶部固定入口(新的朋友、群通知、群聊):
// 图标容器
Container(
width: 40, height: 40,
decoration: BoxDecoration(
color: iconColor, // 橙色/蓝色/绿色
borderRadius: BorderRadius.circular(6),
),
child: Icon(icon, color: Colors.white, size: 22),
)
| 入口 | 图标 | 颜色 |
|---|---|---|
| 新的朋友 | Icons.person_add | #F97D1C 橙色 |
| 群通知 | Icons.notifications | #3B82F6 蓝色 |
| 群聊 | Icons.group | #4CAF50 绿色 |
红点角标:红色圆角背景 + 白色数字,padding horizontal: 8, vertical: 2
入群确认/申请对话框:
AlertDialog + RoundedRectangleBorder(borderRadius: 12)#F8F8F8,圆角 8#999999FilledButton + 圆角 6统一用 ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(...)))
常见文案:
已成功加入群聊申请已发送,等待群主审批已同意 / 已拒绝已开启入群验证 / 已关闭入群验证操作失败:$e红点角标(通讯录 Tab、群通知入口):
Container(
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 1),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(8),
),
child: Text(count > 99 ? '99+' : '$count',
style: TextStyle(color: Colors.white, fontSize: 10)),
)
闪讯的公共组件分布在 flash_shared 和 flash_session 两个模块中。
位置:client/modules/flash_shared/lib/src/
| 组件 | 文件 | 用途 |
|---|---|---|
| IdenticonAvatar | identicon_avatar.dart | 像素风头像,基于 seed 生成 5×5 对称方块图案 |
| AvatarWidget | avatar_widget.dart | 通用头像,自动路由 identicon / 网络图片 / 占位 |
| GroupAvatarWidget | group_avatar_widget.dart | 群组宫格头像,解析 grid: 格式,微信风格布局 |
| WxPopupMenuButton | popup_menu_button.dart | 微信风格右上角弹出菜单,深色气泡 + 尖角 + 动画 |
| FlashSearchBar | search_bar.dart | 搜索栏,占位模式 / 编辑模式两种 |
| FlashSearchInput | search_input.dart | 纯搜索输入框,白色圆角 + 搜索图标 |
AvatarWidget(
avatar: 'identicon:user123', // 或 'http://...' 或 null
size: 44,
borderRadius: 6,
)
路由规则:
identicon:xxx → IdenticonAvatar(像素风)http(s)://... → Image.network(含错误降级)GroupAvatarWidget(
members: [
GroupAvatarMember(id: '1', avatarUrl: 'identicon:1'),
GroupAvatarMember(id: '2', avatarUrl: 'identicon:2'),
],
size: 48,
borderRadius: 6,
)
布局规则:1 人铺满、2 人横排、3 人上 1 下 2、4 人 2×2、5-9 人 3 列网格。
解析 grid: 格式:
final avatar = 'grid:url1,url2,url3';
final avatarList = avatar.substring(5).split(',');
final members = avatarList.asMap().entries.map((e) =>
GroupAvatarMember(id: 'member_${e.key}', avatarUrl: e.value.trim().isNotEmpty ? e.value.trim() : null)
).toList();
WxPopupMenuButton(
items: [
WxMenuItem(icon: Icons.group_add, text: '发起群聊', onTap: () => ...),
WxMenuItem(icon: Icons.person_add, text: '加好友/群', onTap: () => ...),
],
child: Icon(Icons.add_circle_outline, size: 22),
)
深色气泡菜单,缩放 0.8→1.0 + 淡入 200ms 动画,点击背景自动关闭。
// 占位模式(点击跳转)
FlashSearchBar(hintText: '搜索', onTap: () => Navigator.push(...))
// 编辑模式(可输入)
FlashSearchBar(editable: true, controller: ctrl, onChanged: (v) => ...)
高度 36px,背景色 #EDEDED,输入框白色圆角 6px。
位置:client/modules/flash_session/lib/src/view/
| 组件 | 文件 | 用途 |
|---|---|---|
| UserAvatar | widget/user_card.dart | 用户头像,基于 User 对象自动选择渲染方式 |
| UserCard | widget/user_card.dart | 微信风格用户卡片(头像 + 昵称 + 闪讯号 + 签名) |
| SetPasswordPage | set_password_page.dart | 设置密码页面 |
| EditProfilePage | edit_profile_page.dart | 个人资料编辑页面 |
UserAvatar(user: currentUser, size: 64, borderRadius: 8)
和 AvatarWidget 的区别:UserAvatar 直接接收 User 对象,根据 hasCustomAvatar 自动选择 identicon 或网络图片。AvatarWidget 接收字符串。
头像 URL 可能是相对路径,需要拼接 baseUrl。统一的解析方法:
String? _resolveUrl(String? path) {
if (path == null || path.isEmpty) return null;
if (path.startsWith('http') || path.startsWith('identicon:') || path.startsWith('grid:')) return path;
if (baseUrl != null) return '$baseUrl$path';
return path;
}
注意:identicon: 和 grid: 前缀不能拼接 baseUrl,否则会变成无效 URL。