feat: pedir nombre al primer login + avatar con iniciales

- Detectar usuario nuevo (nombre == telefono) y redirigir
  a pantalla setup-name antes del dashboard
- SetupNameView: bienvenida con campo de nombre obligatorio
- AuthProvider.updateName() para guardar via PATCH /users/me
- NavbarAvatar: iniciales con color por inicial del nombre
  en lugar de no-image.jpg cuando no hay foto
- Perfil: mismo avatar de iniciales en la pantalla de perfil

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-06-27 09:29:58 -05:00
co-authored by Claude Sonnet 4.6
parent 7349d4e3fd
commit bfa494b7fe
6 changed files with 272 additions and 20 deletions
+56 -11
View File
@@ -10,23 +10,68 @@ class NavbarAvatar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final user = Provider.of<AuthProvider>(context).user!;
final image = (user.picture == '' || user.picture == null)
? const Image(image: AssetImage('no-image.jpg'))
: FadeInImage.assetNetwork(
placeholder: 'loader.gif',
fit: BoxFit.cover,
image: user.picture!,
);
final hasPicture = user.picture != null && user.picture!.isNotEmpty;
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => NavigationService.replaceTo(Flurorouter.profileRoute),
child: SizedBox(
width: 40,
height: 40,
child: ClipOval(child: image),
width: 36,
height: 36,
child: ClipOval(
child: hasPicture
? Image.network(
user.picture!,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => _InitialsAvatar(name: user.name),
)
: _InitialsAvatar(name: user.name),
),
),
),
);
}
}
class _InitialsAvatar extends StatelessWidget {
final String name;
const _InitialsAvatar({required this.name});
String get _initials {
final parts = name.trim().split(RegExp(r'\s+'));
if (parts.isEmpty || parts.first.isEmpty) return '?';
if (parts.length == 1) return parts.first[0].toUpperCase();
return (parts.first[0] + parts.last[0]).toUpperCase();
}
Color get _bgColor {
final colors = [
const Color(0xFF6366F1), // indigo
const Color(0xFF8B5CF6), // violet
const Color(0xFFEC4899), // pink
const Color(0xFF14B8A6), // teal
const Color(0xFFF59E0B), // amber
const Color(0xFF10B981), // emerald
const Color(0xFFEF4444), // red
const Color(0xFF3B82F6), // blue
];
final index = name.isEmpty ? 0 : name.codeUnitAt(0) % colors.length;
return colors[index];
}
@override
Widget build(BuildContext context) {
return Container(
color: _bgColor,
child: Center(
child: Text(
_initials,
style: const TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.bold,
),
),
),
);