Files
plezy/lib/widgets/user_avatar_widget.dart
T
2025-10-30 19:50:50 +01:00

236 lines
6.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import '../models/plex_home_user.dart';
class UserAvatarWidget extends StatelessWidget {
final PlexHomeUser user;
final double size;
final bool showIndicators;
final bool useTextLabels;
final VoidCallback? onTap;
const UserAvatarWidget({
super.key,
required this.user,
this.size = 40,
this.showIndicators = true,
this.useTextLabels = false,
this.onTap,
});
Widget _buildPlaceholderAvatar(ThemeData theme) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHighest,
shape: BoxShape.circle,
),
child: Icon(
Icons.person,
size: size * 0.6,
color: theme.colorScheme.onSurfaceVariant,
),
);
}
List<Widget> _buildTextLabels(ThemeData theme) {
if (!useTextLabels || !showIndicators) return [];
final labels = <Widget>[];
if (user.isAdminUser) {
labels.add(
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: theme.colorScheme.primary,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Admin',
style: theme.textTheme.labelSmall?.copyWith(
color: theme.colorScheme.onPrimary,
fontWeight: FontWeight.bold,
),
),
),
);
}
if (user.isRestrictedUser && !user.isAdminUser) {
labels.add(
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: theme.colorScheme.warning ?? Colors.orange,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Restricted',
style: theme.textTheme.labelSmall?.copyWith(
color: theme.colorScheme.onPrimary,
fontWeight: FontWeight.bold,
),
),
),
);
}
if (user.requiresPassword) {
labels.add(
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: theme.colorScheme.secondary,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Protected',
style: theme.textTheme.labelSmall?.copyWith(
color: theme.colorScheme.onSecondary,
fontWeight: FontWeight.bold,
),
),
),
);
}
if (labels.isEmpty) return [];
return [
const SizedBox(height: 4),
Wrap(
spacing: 4,
runSpacing: 2,
alignment: WrapAlignment.center,
children: labels,
),
];
}
Widget _buildAvatar(ThemeData theme) {
return SizedBox(
width: size,
height: size,
child: Stack(
children: [
// Avatar image
ClipOval(
child: CachedNetworkImage(
imageUrl: user.thumb,
width: size,
height: size,
fit: BoxFit.cover,
placeholder: (context, url) => _buildPlaceholderAvatar(theme),
errorWidget: (context, url, error) =>
_buildPlaceholderAvatar(theme),
),
),
// Indicators (only show icon indicators when not using text labels)
if (showIndicators && !useTextLabels) ...[
// Admin badge
if (user.isAdminUser)
Positioned(
top: 0,
right: 0,
child: Container(
width: size * 0.3,
height: size * 0.3,
decoration: BoxDecoration(
color: theme.colorScheme.primary,
shape: BoxShape.circle,
border: Border.all(
color: theme.colorScheme.surface,
width: 1,
),
),
child: Icon(
Icons.admin_panel_settings,
size: size * 0.2,
color: theme.colorScheme.onPrimary,
),
),
),
// Restricted badge
if (user.isRestrictedUser && !user.isAdminUser)
Positioned(
top: 0,
right: 0,
child: Container(
width: size * 0.3,
height: size * 0.3,
decoration: BoxDecoration(
color: theme.colorScheme.warning ?? Colors.orange,
shape: BoxShape.circle,
border: Border.all(
color: theme.colorScheme.surface,
width: 1,
),
),
child: Icon(
Icons.security,
size: size * 0.2,
color: theme.colorScheme.onPrimary,
),
),
),
// Password indicator
if (user.requiresPassword)
Positioned(
bottom: 0,
right: 0,
child: Container(
width: size * 0.25,
height: size * 0.25,
decoration: BoxDecoration(
color: theme.colorScheme.secondary,
shape: BoxShape.circle,
border: Border.all(
color: theme.colorScheme.surface,
width: 1,
),
),
child: Icon(
Icons.lock,
size: size * 0.15,
color: theme.colorScheme.onSecondary,
),
),
),
],
],
),
);
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
if (useTextLabels) {
// Return avatar with text labels below
return GestureDetector(
onTap: onTap,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [_buildAvatar(theme), ..._buildTextLabels(theme)],
),
);
} else {
// Return just the avatar (original behavior)
return GestureDetector(onTap: onTap, child: _buildAvatar(theme));
}
}
}
// Extension to add warning color to ColorScheme if not available
extension ColorSchemeExtension on ColorScheme {
Color? get warning => brightness == Brightness.light
? Colors.orange.shade600
: Colors.orange.shade400;
}