Browse Source
- 创建 UnifiedAvatar 组件统一处理所有头像显示逻辑 - 在 ProfileController 中添加 isAvatarReady 状态防止闪烁 - 修改多个视图使用新的统一头像组件 - 优化头像加载流程,确保本地和远程头像一致性weicu
6 changed files with 272 additions and 155 deletions
@ -0,0 +1,137 @@ |
|||
import 'dart:io'; |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
|||
import 'package:get/get.dart'; |
|||
import '../controllers/profile_controller.dart'; |
|||
|
|||
/// 统一头像显示组件 |
|||
/// 确保所有页面的头像显示逻辑一致,避免闪烁问题 |
|||
class UnifiedAvatar extends StatelessWidget { |
|||
final double radius; |
|||
final Color? backgroundColor; |
|||
final Color? iconColor; |
|||
final double? iconSize; |
|||
final bool isDarkMode; |
|||
final VoidCallback? onTap; |
|||
|
|||
const UnifiedAvatar({ |
|||
super.key, |
|||
required this.radius, |
|||
this.backgroundColor, |
|||
this.iconColor, |
|||
this.iconSize, |
|||
this.isDarkMode = false, |
|||
this.onTap, |
|||
}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
final ProfileController profileCtrl = Get.find<ProfileController>(); |
|||
|
|||
return GestureDetector( |
|||
onTap: onTap, |
|||
child: Obx(() { |
|||
// 等待头像数据准备完成,避免闪烁 |
|||
if (!profileCtrl.isAvatarReady.value) { |
|||
return _buildLoadingAvatar(); |
|||
} |
|||
|
|||
return _buildAvatar(profileCtrl); |
|||
}), |
|||
); |
|||
} |
|||
|
|||
/// 构建加载中的头像占位符 |
|||
Widget _buildLoadingAvatar() { |
|||
return CircleAvatar( |
|||
radius: radius, |
|||
backgroundColor: |
|||
backgroundColor ?? (isDarkMode ? Colors.grey[800] : Colors.grey[200]), |
|||
child: Icon( |
|||
Icons.person, |
|||
size: iconSize ?? (radius * 0.6), |
|||
color: iconColor ?? (isDarkMode ? Colors.grey[400] : Colors.grey[600]), |
|||
), |
|||
); |
|||
} |
|||
|
|||
/// 构建实际头像 |
|||
Widget _buildAvatar(ProfileController profileCtrl) { |
|||
ImageProvider<Object>? imageProvider; |
|||
|
|||
// 统一头像显示优先级:本地头像 > 网络头像 > 默认图标 |
|||
if (profileCtrl.localAvatar.value.isNotEmpty) { |
|||
imageProvider = FileImage(File(profileCtrl.localAvatar.value)); |
|||
} else if (profileCtrl.userInfo.value.avatar.isNotEmpty) { |
|||
imageProvider = NetworkImage(profileCtrl.userInfo.value.avatar); |
|||
} |
|||
|
|||
return CircleAvatar( |
|||
radius: radius, |
|||
backgroundImage: imageProvider, |
|||
backgroundColor: |
|||
backgroundColor ?? (isDarkMode ? Colors.grey[800] : Colors.grey[200]), |
|||
child: (profileCtrl.localAvatar.value.isEmpty && |
|||
profileCtrl.userInfo.value.avatar.isEmpty) |
|||
? Icon( |
|||
Icons.person, |
|||
size: iconSize ?? (radius * 0.6), |
|||
color: iconColor ?? |
|||
(isDarkMode ? Colors.grey[400] : Colors.grey[600]), |
|||
) |
|||
: null, |
|||
); |
|||
} |
|||
} |
|||
|
|||
/// 带容器装饰的统一头像组件 |
|||
class UnifiedAvatarWithContainer extends StatelessWidget { |
|||
final double width; |
|||
final double height; |
|||
final double radius; |
|||
final Color? backgroundColor; |
|||
final Color? iconColor; |
|||
final double? iconSize; |
|||
final bool isDarkMode; |
|||
final VoidCallback? onTap; |
|||
final EdgeInsetsGeometry? margin; |
|||
final List<BoxShadow>? boxShadow; |
|||
|
|||
const UnifiedAvatarWithContainer({ |
|||
super.key, |
|||
required this.width, |
|||
required this.height, |
|||
required this.radius, |
|||
this.backgroundColor, |
|||
this.iconColor, |
|||
this.iconSize, |
|||
this.isDarkMode = false, |
|||
this.onTap, |
|||
this.margin, |
|||
this.boxShadow, |
|||
}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Container( |
|||
width: width, |
|||
height: height, |
|||
margin: margin, |
|||
decoration: BoxDecoration( |
|||
borderRadius: BorderRadius.circular(radius), |
|||
boxShadow: boxShadow, |
|||
), |
|||
child: ClipRRect( |
|||
borderRadius: BorderRadius.circular(radius), |
|||
child: UnifiedAvatar( |
|||
radius: radius / 2, |
|||
backgroundColor: backgroundColor, |
|||
iconColor: iconColor, |
|||
iconSize: iconSize, |
|||
isDarkMode: isDarkMode, |
|||
onTap: onTap, |
|||
), |
|||
), |
|||
); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue