perf(mobile-app): optimize avatar loading with parallel fetch and file caching
- Use Future.wait for parallel data loading (faster initial load) - Add file.existsSync() check before using Image.file - Add cacheWidth/cacheHeight and gaplessPlayback for smoother display - Add _checkLocalAvatarSync for early avatar path detection 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
3e01f69044
commit
8eefd807c3
|
|
@ -36,17 +36,40 @@ class _MiningPageState extends ConsumerState<MiningPage> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// 先同步检查本地头像,再异步加载其他数据
|
||||
_checkLocalAvatarSync();
|
||||
_loadUserData();
|
||||
}
|
||||
|
||||
/// 同步检查本地头像文件(在 build 之前快速获取)
|
||||
void _checkLocalAvatarSync() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
final accountService = ref.read(accountServiceProvider);
|
||||
final localPath = await accountService.getLocalAvatarPath();
|
||||
if (mounted && localPath != null && _localAvatarPath == null) {
|
||||
setState(() {
|
||||
_localAvatarPath = localPath;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 加载用户数据
|
||||
Future<void> _loadUserData() async {
|
||||
final accountService = ref.read(accountServiceProvider);
|
||||
|
||||
final serialNum = await accountService.getUserSerialNum();
|
||||
final avatarSvg = await accountService.getAvatarSvg();
|
||||
final avatarUrl = await accountService.getAvatarUrl();
|
||||
final localAvatarPath = await accountService.getLocalAvatarPath();
|
||||
// 并行加载所有数据
|
||||
final results = await Future.wait([
|
||||
accountService.getUserSerialNum(),
|
||||
accountService.getAvatarSvg(),
|
||||
accountService.getAvatarUrl(),
|
||||
accountService.getLocalAvatarPath(),
|
||||
]);
|
||||
|
||||
final serialNum = results[0] as int?;
|
||||
final avatarSvg = results[1] as String?;
|
||||
final avatarUrl = results[2] as String?;
|
||||
final localAvatarPath = results[3] as String?;
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
|
|
@ -386,16 +409,22 @@ class _MiningPageState extends ConsumerState<MiningPage> {
|
|||
// 1. 优先显示本地缓存的头像文件
|
||||
if (_localAvatarPath != null && _localAvatarPath!.isNotEmpty) {
|
||||
final file = File(_localAvatarPath!);
|
||||
return Image.file(
|
||||
file,
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
// 本地文件加载失败,尝试网络URL
|
||||
return _buildNetworkOrSvgAvatar();
|
||||
},
|
||||
);
|
||||
// 同步检查文件是否存在
|
||||
if (file.existsSync()) {
|
||||
return Image.file(
|
||||
file,
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
cacheWidth: 160, // 2x 分辨率缓存,提升性能
|
||||
cacheHeight: 160,
|
||||
gaplessPlayback: true, // 防止图片切换时闪烁
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
// 本地文件加载失败,尝试网络URL
|
||||
return _buildNetworkOrSvgAvatar();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 没有本地缓存,尝试网络URL或SVG
|
||||
|
|
|
|||
|
|
@ -64,18 +64,43 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
_startCountdown();
|
||||
// 先同步检查本地头像,再异步加载其他数据
|
||||
_checkLocalAvatarSync();
|
||||
_loadUserData();
|
||||
}
|
||||
|
||||
/// 同步检查本地头像文件(在 build 之前快速获取)
|
||||
void _checkLocalAvatarSync() {
|
||||
// 使用 WidgetsBinding 确保在第一帧渲染前执行
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
final accountService = ref.read(accountServiceProvider);
|
||||
final localPath = await accountService.getLocalAvatarPath();
|
||||
if (mounted && localPath != null && _localAvatarPath == null) {
|
||||
setState(() {
|
||||
_localAvatarPath = localPath;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 加载用户数据
|
||||
Future<void> _loadUserData() async {
|
||||
final accountService = ref.read(accountServiceProvider);
|
||||
|
||||
final username = await accountService.getUsername();
|
||||
final serialNum = await accountService.getUserSerialNum();
|
||||
final avatarSvg = await accountService.getAvatarSvg();
|
||||
final avatarUrl = await accountService.getAvatarUrl();
|
||||
final localAvatarPath = await accountService.getLocalAvatarPath();
|
||||
// 并行加载所有数据
|
||||
final results = await Future.wait([
|
||||
accountService.getUsername(),
|
||||
accountService.getUserSerialNum(),
|
||||
accountService.getAvatarSvg(),
|
||||
accountService.getAvatarUrl(),
|
||||
accountService.getLocalAvatarPath(),
|
||||
]);
|
||||
|
||||
final username = results[0] as String?;
|
||||
final serialNum = results[1] as int?;
|
||||
final avatarSvg = results[2] as String?;
|
||||
final avatarUrl = results[3] as String?;
|
||||
final localAvatarPath = results[4] as String?;
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
|
|
@ -365,16 +390,22 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
|||
// 1. 优先显示本地缓存的头像文件
|
||||
if (_localAvatarPath != null && _localAvatarPath!.isNotEmpty) {
|
||||
final file = File(_localAvatarPath!);
|
||||
return Image.file(
|
||||
file,
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
// 本地文件加载失败,尝试网络URL
|
||||
return _buildNetworkOrSvgAvatar();
|
||||
},
|
||||
);
|
||||
// 同步检查文件是否存在
|
||||
if (file.existsSync()) {
|
||||
return Image.file(
|
||||
file,
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
cacheWidth: 160, // 2x 分辨率缓存,提升性能
|
||||
cacheHeight: 160,
|
||||
gaplessPlayback: true, // 防止图片切换时闪烁
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
// 本地文件加载失败,尝试网络URL
|
||||
return _buildNetworkOrSvgAvatar();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 没有本地缓存,尝试网络URL或SVG
|
||||
|
|
|
|||
Loading…
Reference in New Issue