二次开发源码提交
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import 'dart:io';
|
||||
import 'package:cloudreve4_flutter/presentation/providers/auth_provider.dart';
|
||||
import 'package:cloudreve4_flutter/services/avatar_cache_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
/// 用户头像组件
|
||||
/// 1. 缓存存在 → 直接 FileImage 显示
|
||||
/// 2. 缓存不存在 → 默认显示首字母 fallback,异步 getAvatar,成功后更新 UI
|
||||
class UserAvatar extends StatefulWidget {
|
||||
final String userId;
|
||||
final String? email;
|
||||
final String displayName;
|
||||
final double radius;
|
||||
final String? avatarType;
|
||||
|
||||
const UserAvatar({
|
||||
super.key,
|
||||
required this.userId,
|
||||
this.email,
|
||||
required this.displayName,
|
||||
this.radius = 24,
|
||||
this.avatarType,
|
||||
});
|
||||
|
||||
@override
|
||||
State<UserAvatar> createState() => _UserAvatarState();
|
||||
|
||||
/// 清除指定用户的头像缓存(头像上传/切换后调用)
|
||||
static Future<void> evictCache(String userId) async {
|
||||
await AvatarCacheService.instance.evictCache(userId);
|
||||
}
|
||||
|
||||
/// 清除所有头像缓存(登出时调用)
|
||||
static Future<void> clearAllCache() async {
|
||||
await AvatarCacheService.instance.clearAllCache();
|
||||
}
|
||||
}
|
||||
|
||||
class _UserAvatarState extends State<UserAvatar> {
|
||||
File? _avatarFile;
|
||||
bool _loadTriggered = false;
|
||||
|
||||
String get _initial {
|
||||
final name = widget.displayName;
|
||||
return name.isNotEmpty ? name[0].toUpperCase() : 'U';
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_checkCache();
|
||||
AvatarCacheService.instance.addListener(_onServiceNotify);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(UserAvatar oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.userId != widget.userId || oldWidget.email != widget.email) {
|
||||
_avatarFile = null;
|
||||
_loadTriggered = false;
|
||||
_checkCache();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
AvatarCacheService.instance.removeListener(_onServiceNotify);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _checkCache() {
|
||||
if (widget.userId.isEmpty) return;
|
||||
final file = AvatarCacheService.instance.getCachedFile(widget.userId);
|
||||
if (file != null) {
|
||||
_avatarFile = file;
|
||||
}
|
||||
}
|
||||
|
||||
void _onServiceNotify() {
|
||||
final file = AvatarCacheService.instance.getCachedFile(widget.userId);
|
||||
if (file != null && _avatarFile?.path != file.path) {
|
||||
setState(() => _avatarFile = file);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadIfMissing() async {
|
||||
if (_loadTriggered || widget.userId.isEmpty) return;
|
||||
_loadTriggered = true;
|
||||
|
||||
final auth = context.read<AuthProvider>();
|
||||
await AvatarCacheService.instance.getAvatar(
|
||||
widget.userId,
|
||||
baseUrl: auth.currentServer?.baseUrl,
|
||||
token: auth.token?.accessToken,
|
||||
email: widget.email,
|
||||
);
|
||||
// getAvatar 成功后 notifyListeners → _onServiceNotify 更新 _avatarFile
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
// 有缓存文件,显示图片
|
||||
if (_avatarFile != null) {
|
||||
return CircleAvatar(
|
||||
radius: widget.radius,
|
||||
backgroundColor: colorScheme.primaryContainer,
|
||||
backgroundImage: FileImage(_avatarFile!),
|
||||
);
|
||||
}
|
||||
|
||||
// 缓存不存在,触发异步加载
|
||||
_loadIfMissing();
|
||||
|
||||
// Fallback: 首字母(默认展示,避免空白)
|
||||
return CircleAvatar(
|
||||
radius: widget.radius,
|
||||
backgroundColor: colorScheme.primaryContainer,
|
||||
child: Text(
|
||||
_initial,
|
||||
style: TextStyle(
|
||||
fontSize: widget.radius * 0.75,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user