Files
tongxunruanjian/mobile/lib/widgets/avatar.dart
T
KIMI b95159f7eb 新增 mobile/:畅联手机端 Flutter 工程(B-58)
- 登录:工号+密码走 account-service /api/login,自动登录、被踢下线回登录页
- 消息:会话列表(未读角标/免打扰/时间)、单聊群聊、文字/语音/图片/文件消息、失败重发、历史分页
- 通讯录:新的同事/我的群聊入口、按部门分组(好友 ex 字段)、搜索
- 通话:一对一语音通话(信令走 OpenIM 自定义消息 + LiveKit,token 走 /api/rtc_token)
- 界面按确认效果图 m1/m2/m3 实现,主色 #3B87F5,四态视图齐全,无英文残留
2026-08-09 01:28:12 +08:00

103 lines
2.7 KiB
Dart

import 'package:flutter/material.dart';
import '../theme.dart';
/// 头像(按效果图):
/// - 单人是圆角方块 + 姓氏首字,蓝 / 灰蓝两色按名字 hash 取色;
/// - 群聊是 2x2 小方块拼格;
/// - 有 faceURL 时优先显示网络头像,加载失败回退到首字。
class NameAvatar extends StatelessWidget {
final String name;
final String? faceURL;
final double size;
/// 是否群聊样式(2x2 拼格)
final bool isGroup;
const NameAvatar({
super.key,
required this.name,
this.faceURL,
this.size = 48,
this.isGroup = false,
});
Color _colorFor(String text) {
var hash = 0;
for (final code in text.codeUnits) {
hash = (hash * 31 + code) & 0x7fffffff;
}
return hash % 2 == 0 ? AppColors.primary : AppColors.avatarGray;
}
@override
Widget build(BuildContext context) {
final radius = BorderRadius.circular(size * 0.18);
if (isGroup) return _groupAvatar(radius);
final hasFace = faceURL != null && faceURL!.isNotEmpty;
final letter = name.isNotEmpty ? name.substring(0, 1) : '';
return ClipRRect(
borderRadius: radius,
child: Container(
width: size,
height: size,
color: _colorFor(name),
alignment: Alignment.center,
child: hasFace
? Image.network(
faceURL!,
width: size,
height: size,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => _letterText(letter),
loadingBuilder: (_, child, progress) => progress == null ? child : _letterText(letter),
)
: _letterText(letter),
),
);
}
Widget _letterText(String letter) {
return Text(
letter,
style: TextStyle(
color: Colors.white,
fontSize: size * 0.4,
fontWeight: FontWeight.w500,
),
);
}
/// 群头像:浅灰底上 2x2 小方块,蓝 / 灰蓝交替
Widget _groupAvatar(BorderRadius radius) {
final cell = (size - 10) / 2;
const colors = [AppColors.primary, AppColors.avatarGray, AppColors.avatarGray, AppColors.primary];
return ClipRRect(
borderRadius: radius,
child: Container(
width: size,
height: size,
color: AppColors.searchBg,
padding: const EdgeInsets.all(2),
child: Wrap(
spacing: 2,
runSpacing: 2,
children: List.generate(
4,
(i) => Container(
width: cell,
height: cell,
decoration: BoxDecoration(
color: colors[i],
borderRadius: BorderRadius.circular(2),
),
),
),
),
),
);
}
}