Files
sendico/frontend/pweb/lib/pages/settings/profile/account/avatar.dart
2025-12-22 21:09:58 +03:00

137 lines
4.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:image_picker/image_picker.dart';
import 'package:pshared/provider/account.dart';
import 'package:pweb/widgets/drawer/avatar.dart';
class AvatarTile extends StatefulWidget {
final String? avatarUrl;
final String title;
final String description;
final String errorText;
const AvatarTile({
super.key,
required this.avatarUrl,
required this.title,
required this.description,
required this.errorText,
});
@override
State<AvatarTile> createState() => _AvatarTileState();
}
class _AvatarTileState extends State<AvatarTile> {
static const double _avatarSize = 96.0;
static const double _iconSize = 32.0;
static const double _titleSpacing = 4.0;
bool _isHovering = false;
bool _isUploading = false;
String _errorText = '';
Future<void> _pickImage(AccountProvider provider) async {
if (_isUploading) return;
final picker = ImagePicker();
final file = await picker.pickImage(source: ImageSource.gallery);
if (file == null) return;
setState(() {
_isUploading = true;
_errorText = '';
});
try {
await provider.uploadAvatar(file);
} catch (_) {
if (!mounted) return;
setState(() => _errorText = widget.errorText);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(widget.errorText)),
);
} finally {
if (mounted) {
setState(() => _isUploading = false);
}
}
}
@override
Widget build(BuildContext context) {
return Consumer<AccountProvider>(
builder: (context, provider, _) {
final theme = Theme.of(context);
final isBusy = _isUploading || provider.isLoading;
return Column(
children: [
MouseRegion(
onEnter: (_) => setState(() => _isHovering = true),
onExit: (_) => setState(() => _isHovering = false),
child: GestureDetector(
onTap: isBusy ? null : () => _pickImage(provider),
child: Stack(
alignment: Alignment.center,
children: [
AccountAvatar(
size: _avatarSize,
showHeader: false,
provider: provider,
fallbackUrl: widget.avatarUrl,
),
if (_isHovering || _isUploading)
ClipOval(
child: Container(
width: _avatarSize,
height: _avatarSize,
color: theme.colorScheme.primary.withAlpha(90),
child: _isUploading
? SizedBox(
width: _iconSize,
height: _iconSize,
child: CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation(theme.colorScheme.onSecondary),
),
)
: Icon(
Icons.camera_alt,
color: theme.colorScheme.onSecondary,
size: _iconSize,
),
),
),
],
),
),
),
SizedBox(height: _titleSpacing),
Text(
widget.description,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSecondary,
),
),
if (_errorText.isNotEmpty) ...[
SizedBox(height: _titleSpacing),
Text(
_errorText,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.error,
),
),
],
],
);
},
);
}
}