cyberhybridhub/lib/widgets/profile_avatar.dart
2026-05-31 11:17:12 -05:00

82 lines
2.1 KiB
Dart

import 'package:flutter/material.dart';
import '../theme/app_theme.dart';
/// User profile photo with a graceful fallback when the URL fails (e.g.
/// Google `lh3.googleusercontent.com` returning HTTP 429).
class ProfileAvatar extends StatelessWidget {
const ProfileAvatar({
super.key,
this.photoUrl,
this.radius = 36,
});
final String? photoUrl;
final double radius;
@override
Widget build(BuildContext context) {
final String? url = photoUrl?.trim();
if (url == null || url.isEmpty) {
return _fallbackAvatar();
}
final double size = radius * 2;
return ClipOval(
child: SizedBox(
width: size,
height: size,
child: Image.network(
url,
width: size,
height: size,
fit: BoxFit.cover,
// Prevents NetworkImageLoadException from bubbling to the console
// when Google rate-limits profile photo URLs.
errorBuilder: (_, Object error, StackTrace? stackTrace) {
return _fallbackContents();
},
loadingBuilder: (
BuildContext context,
Widget child,
ImageChunkEvent? progress,
) {
if (progress == null) {
return child;
}
return ColoredBox(
color: AppColors.surfaceElevated,
child: Center(
child: SizedBox(
width: radius * 0.55,
height: radius * 0.55,
child: CircularProgressIndicator(
strokeWidth: 2,
color: AppColors.accent.withValues(alpha: 0.7),
),
),
),
);
},
),
),
);
}
Widget _fallbackAvatar() {
return CircleAvatar(
radius: radius,
backgroundColor: AppColors.surfaceElevated,
child: _fallbackContents(),
);
}
Widget _fallbackContents() {
return Icon(
Icons.person,
size: radius,
color: AppColors.accent.withValues(alpha: 0.85),
);
}
}