80 lines
2.4 KiB
Dart
80 lines
2.4 KiB
Dart
import 'package:flutter/material.dart';
|
||
|
||
import '../core/network/api_env.dart';
|
||
import 'cute_icon.dart';
|
||
|
||
/// 圆形头像。全 App 只有这一个头像组件 —— 人设、用户、「我的」页面、聊天气泡都用它。
|
||
///
|
||
/// 三种输入都要兜住:
|
||
/// · [assetPath] 为空 → 画默认图标([fallbackIcon])
|
||
/// · 图片加载中 → 显示一个淡淡的占位圆
|
||
/// · 加载失败(后端文件被删了 / 网络断了)→ 退回默认图标,不显示裂图
|
||
///
|
||
/// 为什么不用 `CircleAvatar`:它的 backgroundImage 出错时不好接管,
|
||
/// 会有「裂图 + 背景色」的难看状态。自己用 [ClipOval] + [Image.network] 能完全控制。
|
||
class Avatar extends StatelessWidget {
|
||
const Avatar({
|
||
super.key,
|
||
this.assetPath,
|
||
this.size = 40,
|
||
this.fallbackIcon = 'user',
|
||
this.fallbackColor = const Color(0xFFC8956E),
|
||
this.backgroundColor = const Color(0xFFFFF3E6),
|
||
});
|
||
|
||
/// 后端返回的**相对路径**(如 `persona/2026/09/29/xxx.jpg`)。
|
||
/// 传空或 null → 用默认图标。
|
||
final String? assetPath;
|
||
|
||
final double size;
|
||
|
||
/// 没有头像时显示哪个小图标(`CuteIcon` 的名字)
|
||
final String fallbackIcon;
|
||
|
||
/// 默认图标的颜色
|
||
final Color fallbackColor;
|
||
|
||
/// 头像底圈的颜色
|
||
final Color backgroundColor;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final path = assetPath?.trim() ?? '';
|
||
|
||
return Container(
|
||
width: size,
|
||
height: size,
|
||
decoration: BoxDecoration(
|
||
shape: BoxShape.circle,
|
||
color: backgroundColor,
|
||
),
|
||
clipBehavior: Clip.antiAlias,
|
||
child: path.isEmpty ? _fallback() : _image(path),
|
||
);
|
||
}
|
||
|
||
Widget _fallback() {
|
||
return Center(
|
||
child: CuteIcon(fallbackIcon, size: size * 0.58),
|
||
);
|
||
}
|
||
|
||
Widget _image(String path) {
|
||
return Image.network(
|
||
resolveAssetUrl('/uploads/$path'),
|
||
fit: BoxFit.cover,
|
||
// 头像显示尺寸很小,限一下解码尺寸省内存 —— 原图可能是 400px
|
||
cacheWidth: (size * 3).round(),
|
||
loadingBuilder: (context, child, progress) {
|
||
// 还没加载出来时给个纯色圆,比空白或转圈干净
|
||
if (progress == null) return child;
|
||
return const SizedBox.shrink();
|
||
},
|
||
errorBuilder: (context, _, _) {
|
||
// 后端文件被删了也会走到这 —— 退回默认图标,不留裂图
|
||
return _fallback();
|
||
},
|
||
);
|
||
}
|
||
}
|