Overview
The User Management screen provides comprehensive tools for managing all platform users including clients, drivers, and company accounts.
Accessing User Management
Navigate to Management Tab
From the admin panel, select the “Gestión” tab
Select User Management
Tap on “Gestión de Usuarios” card
View User List
All users display with filtering and search options
User Management Provider
User management uses a dedicated provider for state management:
admin/presentation/providers/user_management_provider.dart
class AdminUserManagementProvider with ChangeNotifier {
final GetUsersUseCase getUsersUseCase;
final ManageUserUseCase manageUserUseCase;
final int adminId;
List<dynamic> _users = [];
String? _currentFilter; // 'cliente', 'conductor', 'empresa', or null
String? _searchQuery;
bool _showInactive = false; // false = active, true = inactive
Future<void> loadUsers({bool refresh = false}) async {
final result = await getUsersUseCase(
adminId: adminId,
page: _currentPage,
search: _searchQuery,
tipoUsuario: _currentFilter,
esActivo: !_showInactive,
);
}
}
Search and Filters
Search Bar
Search users by name, email, or phone number:
admin/presentation/screens/users_management_screen.dart
Widget _buildSearchBar() {
return Container(
decoration: BoxDecoration(
color: AppColors.darkSurface.withValues(alpha: 0.8),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: Colors.white12),
),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
hintText: 'Buscar por nombre, email o teléfono...',
prefixIcon: Icon(Icons.search_rounded),
suffixIcon: _searchController.text.isNotEmpty
? IconButton(
icon: Icon(Icons.clear_rounded),
onPressed: () {
_searchController.clear();
_provider.setSearchQuery('');
},
)
: null,
),
onSubmitted: (value) => _provider.setSearchQuery(value),
),
);
}
User Type Filters
All Users
Clients
Drivers
Companies
Display all users regardless of typeFilter Value: null
Show only passenger accountsFilter Value: 'cliente'Color: Green (#11998e)
Show only driver accountsFilter Value: 'conductor'Color: Blue (#667eea)
Show only company accountsFilter Value: 'empresa'Color: Orange (#ffa726)
Active/Inactive Toggle
Switch between viewing active or inactive users:
admin/presentation/screens/users_management_screen.dart
GestureDetector(
onTap: () {
_provider.setShowInactive(!_provider.showInactive);
setState(() {});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
decoration: BoxDecoration(
color: _provider.showInactive
? Colors.red.withValues(alpha: 0.15)
: AppColors.success.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: _provider.showInactive ? Colors.red : AppColors.success,
),
),
child: Row(
children: [
Icon(
_provider.showInactive
? Icons.person_off_rounded
: Icons.person_rounded,
),
Text(_provider.showInactive ? 'Inactivos' : 'Activos'),
],
),
),
)
Filter Chip Implementation
admin/presentation/screens/users_management_screen.dart
Widget _buildFilterChip({
required String label,
required bool isSelected,
Color? color,
required VoidCallback onTap,
}) {
final chipColor = color ?? AppColors.primary;
return GestureDetector(
onTap: onTap,
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: isSelected
? chipColor.withValues(alpha: 0.15)
: Colors.transparent,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: isSelected ? chipColor : Colors.grey.withValues(alpha: 0.3),
),
),
child: Text(label),
),
);
}
User List Display
Users are displayed in an interactive, scrollable list:
admin/presentation/screens/users_management_screen.dart
Widget _buildUsersList(AdminUserManagementProvider provider) {
return RefreshIndicator(
onRefresh: () async => provider.loadUsers(refresh: true),
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16),
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
),
itemCount: provider.users.length,
itemBuilder: (context, index) {
final user = provider.users[index];
return UserCard(
user: user,
onTap: () => _showUserDetails(user),
onEdit: () => _showUserEdit(provider, user),
onToggleStatus: () => _showStatusConfirmation(provider, user),
);
},
),
);
}
Each user is displayed in a custom card component with key information:
admin/presentation/widgets/user_management_widgets.dart
class UserCard extends StatelessWidget {
final Map<String, dynamic> user;
final VoidCallback? onTap;
final VoidCallback? onEdit;
final VoidCallback? onToggleStatus;
@override
Widget build(BuildContext context) {
final isActive = user['es_activo'] == 1;
final tipoUsuario = user['tipo_usuario']?.toString() ?? 'cliente';
return Card(
child: ListTile(
leading: CircleAvatar(
backgroundImage: user['foto_perfil'] != null
? NetworkImage(user['foto_perfil'])
: null,
child: user['foto_perfil'] == null
? Icon(_getUserIcon(tipoUsuario))
: null,
),
title: Text('${user['nombre']} ${user['apellido']}'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(user['email'] ?? ''),
Row(
children: [
_buildStatusBadge(isActive),
_buildTypeBadge(tipoUsuario),
],
),
],
),
trailing: PopupMenuButton(
itemBuilder: (context) => [
PopupMenuItem(
child: Text('Ver detalles'),
onTap: onTap,
),
PopupMenuItem(
child: Text('Editar'),
onTap: onEdit,
),
PopupMenuItem(
child: Text(isActive ? 'Desactivar' : 'Activar'),
onTap: onToggleStatus,
),
],
),
),
);
}
}
User Details Sheet
View complete user information in a modal bottom sheet:
admin/presentation/widgets/user_management_widgets.dart
class UserDetailsSheet extends StatelessWidget {
final Map<String, dynamic> user;
@override
Widget build(BuildContext context) {
return DraggableScrollableSheet(
initialChildSize: 0.7,
minChildSize: 0.5,
maxChildSize: 0.95,
builder: (context, scrollController) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
),
child: SingleChildScrollView(
controller: scrollController,
child: Column(
children: [
_buildHeader(),
_buildPersonalInfo(),
_buildContactInfo(),
_buildAccountInfo(),
if (user['tipo_usuario'] == 'conductor')
_buildDriverInfo(),
if (user['empresa_nombre'] != null)
_buildCompanyInfo(),
],
),
),
);
},
);
}
}
Edit User Sheet
Modify user information through an edit form:
admin/presentation/widgets/user_management_widgets.dart
class UserEditSheet extends StatefulWidget {
final Map<String, dynamic> user;
final Function(String? nombre, String? apellido, String? telefono,
String? tipoUsuario, int? empresaId, String? empresaNombre) onSave;
@override
State<UserEditSheet> createState() => _UserEditSheetState();
}
class _UserEditSheetState extends State<UserEditSheet> {
late TextEditingController _nombreController;
late TextEditingController _apellidoController;
late TextEditingController _telefonoController;
String? _selectedTipo;
int? _selectedEmpresaId;
@override
void initState() {
super.initState();
_nombreController = TextEditingController(text: widget.user['nombre']);
_apellidoController = TextEditingController(text: widget.user['apellido']);
_telefonoController = TextEditingController(text: widget.user['telefono']);
_selectedTipo = widget.user['tipo_usuario'];
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(24),
child: Column(
children: [
TextField(
controller: _nombreController,
decoration: InputDecoration(labelText: 'Nombre'),
),
TextField(
controller: _apellidoController,
decoration: InputDecoration(labelText: 'Apellido'),
),
TextField(
controller: _telefonoController,
decoration: InputDecoration(labelText: 'Teléfono'),
),
DropdownButtonFormField<String>(
value: _selectedTipo,
items: ['cliente', 'conductor', 'empresa']
.map((tipo) => DropdownMenuItem(
value: tipo,
child: Text(tipo),
))
.toList(),
onChanged: (value) => setState(() => _selectedTipo = value),
),
ElevatedButton(
onPressed: () {
widget.onSave(
_nombreController.text,
_apellidoController.text,
_telefonoController.text,
_selectedTipo,
_selectedEmpresaId,
null,
);
Navigator.pop(context);
},
child: Text('Guardar Cambios'),
),
],
),
);
}
}
Update User Implementation
admin/presentation/providers/user_management_provider.dart
Future<bool> updateUser({
required int userId,
String? nombre,
String? apellido,
String? telefono,
String? tipoUsuario,
int? empresaId,
String? empresaNombre,
}) async {
_setLoading(true);
try {
final result = await manageUserUseCase.updateUser(
adminId: adminId,
userId: userId,
nombre: nombre,
apellido: apellido,
telefono: telefono,
tipoUsuario: tipoUsuario,
empresaId: empresaId,
);
return result.fold(
(failure) {
_setError('Error al actualizar: ${failure.toString()}');
return false;
},
(success) {
if (success) {
// Update local list
final index = _users.indexWhere((u) => u['id'] == userId);
if (index != -1) {
final updatedUser = Map<String, dynamic>.from(_users[index]);
if (nombre != null) updatedUser['nombre'] = nombre;
if (apellido != null) updatedUser['apellido'] = apellido;
if (telefono != null) updatedUser['telefono'] = telefono;
if (tipoUsuario != null) updatedUser['tipo_usuario'] = tipoUsuario;
if (empresaId != null && empresaId > 0) {
updatedUser['empresa_id'] = empresaId;
}
_users[index] = updatedUser;
notifyListeners();
}
}
_setLoading(false);
return success;
},
);
} catch (e) {
_setError('Error inesperado: $e');
return false;
}
}
Toggle User Status
Activate or deactivate user accounts:
admin/presentation/screens/users_management_screen.dart
void _showStatusConfirmation(AdminUserManagementProvider provider, Map<String, dynamic> user) {
final isActivating = user['es_activo'] == 0;
final action = isActivating ? 'activar' : 'desactivar';
final color = isActivating ? AppColors.success : AppColors.warning;
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
isActivating ? Icons.play_circle_outline : Icons.pause_circle_outline,
color: color,
),
),
Text('${action[0].toUpperCase()}${action.substring(1)} Usuario'),
],
),
content: Text('¿Estás seguro de que deseas $action a "${user['nombre']}"?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancelar'),
),
ElevatedButton(
onPressed: () async {
Navigator.pop(context);
await provider.toggleUserStatus(user['id'], !isActivating);
},
style: ElevatedButton.styleFrom(backgroundColor: color),
child: Text(isActivating ? 'Activar' : 'Desactivar'),
),
],
),
);
}
Status Toggle Implementation
admin/presentation/providers/user_management_provider.dart
Future<bool> toggleUserStatus(int userId, bool currentStatus) async {
final result = await manageUserUseCase.updateUser(
adminId: adminId,
userId: userId,
esActivo: !currentStatus,
);
return result.fold(
(failure) {
_setError("Error al actualizar estado");
return false;
},
(success) {
if (success) {
// Update local list
final index = _users.indexWhere((u) => u['id'] == userId);
if (index != -1) {
_users[index]['es_activo'] = !currentStatus ? 1 : 0;
notifyListeners();
}
}
return success;
},
);
}
Empty State
admin/presentation/screens/users_management_screen.dart
Widget _buildEmptyState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: AppColors.primary.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: const Icon(
Icons.people_outline_rounded,
color: AppColors.primary,
size: 48,
),
),
const SizedBox(height: 16),
Text('No hay usuarios'),
Text('No se encontraron usuarios con los filtros actuales'),
],
),
);
}
All changes to user data are automatically reflected in the UI without requiring a page refresh.
Best Practices
Combine user type filters with active/inactive toggle to quickly find specific user groups.
Verify Before Deactivating
Always review user details before deactivating accounts to avoid accidental disruptions.
Assign Companies Properly
For drivers, ensure they’re assigned to the correct transport company for proper commission tracking.
Monitor Inactive Accounts
Regularly review inactive accounts to identify issues or re-engage dormant users.
Driver Management
Verify and approve drivers
Company Management
Manage transport companies
Audit Logs
Track user changes