avatar_image_widget.dart 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import 'package:cached_network_image/cached_network_image.dart';
  2. import 'package:flutter/material.dart';
  3. /// 圆形头像组件
  4. class CircleAvatarWidget extends StatelessWidget {
  5. /// 本地图片
  6. final ImageProvider? image;
  7. /// 网络图片URL
  8. final String? imageUrl;
  9. /// 加载失败时的占位图
  10. final ImageProvider? placeholderImage;
  11. /// 头像大小
  12. final double size;
  13. /// 边框颜色
  14. final Color borderColor;
  15. /// 边框宽度
  16. final double borderWidth;
  17. /// 背景颜色
  18. final Color backgroundColor;
  19. /// 加载时的渐变时长
  20. final Duration fadeInDuration;
  21. /// 加载中的占位
  22. final PlaceholderWidgetBuilder? placeholder;
  23. const CircleAvatarWidget({
  24. super.key,
  25. this.image,
  26. this.imageUrl,
  27. this.placeholderImage,
  28. this.size = 40.0,
  29. this.borderColor = Colors.white,
  30. this.borderWidth = 2.0,
  31. this.backgroundColor = const Color(0xFFEEEEEE),
  32. this.fadeInDuration = const Duration(milliseconds: 500),
  33. this.placeholder,
  34. });
  35. @override
  36. Widget build(BuildContext context) {
  37. return Container(
  38. width: size,
  39. height: size,
  40. decoration: BoxDecoration(
  41. shape: BoxShape.circle,
  42. border: Border.all(color: borderColor, width: borderWidth),
  43. ),
  44. // 裁切圆形
  45. child: ClipOval(child: _buildContent()),
  46. );
  47. }
  48. /// 构建头像内容
  49. Widget _buildContent() {
  50. // 优先使用网络图片
  51. if (imageUrl != null && imageUrl!.isNotEmpty) {
  52. return CachedNetworkImage(
  53. imageUrl: imageUrl!,
  54. width: size,
  55. height: size,
  56. fit: BoxFit.cover,
  57. fadeInDuration: fadeInDuration,
  58. placeholder: placeholder ?? _buildLoadingPlaceholder,
  59. errorWidget: (_, __, ___) => _buildPlaceholder(), // 错误时显示占位
  60. );
  61. }
  62. // 没有,再使用本地图片
  63. else if (image != null) {
  64. return Image(
  65. image: image!,
  66. width: size,
  67. height: size,
  68. fit: BoxFit.cover,
  69. errorBuilder: (_, __, ___) => _buildPlaceholder(),
  70. );
  71. }
  72. // 无图片状态
  73. else {
  74. return _buildPlaceholder();
  75. }
  76. }
  77. /// 加载中占位
  78. Widget _buildLoadingPlaceholder(BuildContext context, String url) {
  79. return Container(
  80. color: Colors.grey[200],
  81. alignment: Alignment.center,
  82. child: const CircularProgressIndicator(strokeWidth: 2),
  83. );
  84. }
  85. /// 错误/默认的占位组件
  86. Widget _buildPlaceholder() {
  87. // 优先使用自定义占位图
  88. if (placeholderImage != null) {
  89. return Image(
  90. image: placeholderImage!,
  91. width: size,
  92. height: size,
  93. fit: BoxFit.cover,
  94. // 占位图加载失败时,显示一个纯色背景
  95. errorBuilder: (_, __, ___) => _buildDefaultPlaceholder(),
  96. );
  97. }
  98. // 默认占位
  99. return _buildDefaultPlaceholder();
  100. }
  101. /// 占位图加载失败时,显示一个纯色背景
  102. Widget _buildDefaultPlaceholder() {
  103. return Container(color: backgroundColor, width: size, height: size);
  104. }
  105. }