Skip to main content

Overview

The User Management screen provides comprehensive tools for managing all platform users including clients, drivers, and company accounts.

Accessing User Management

1

Navigate to Management Tab

From the admin panel, select the “Gestión” tab
2

Select User Management

Tap on “Gestión de Usuarios” card
3

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 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

Display all users regardless of typeFilter Value: null

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),
        );
      },
    ),
  );
}

User Card Widget

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.
Always review user details before deactivating accounts to avoid accidental disruptions.
For drivers, ensure they’re assigned to the correct transport company for proper commission tracking.
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

Build docs developers (and LLMs) love