diff --git a/lib/providers/professionals_provider.dart b/lib/providers/professionals_provider.dart index 73e0255..d3f0d6d 100644 --- a/lib/providers/professionals_provider.dart +++ b/lib/providers/professionals_provider.dart @@ -5,25 +5,23 @@ import 'package:prosapp_web_app/services/api_service.dart'; class ProfessionalsProvider extends ChangeNotifier { List professionals = []; bool isLoading = true; - String? _city; bool _initialized = false; final _api = ApiService.instance; void updateCity(String? city) { - // Always load on first auth event; afterwards only reload when city changes. - if (!_initialized || _city != city) { + // Only used to trigger the initial load when auth becomes available. + if (!_initialized) { _initialized = true; - _city = city; getProfessionals(); } } - getProfessionals() async { + Future getProfessionals({String? search}) async { isLoading = true; notifyListeners(); try { - final path = (_city != null && _city!.isNotEmpty) - ? '/professionals?city=${Uri.encodeComponent(_city!)}' + final path = (search != null && search.trim().isNotEmpty) + ? '/professionals?search=${Uri.encodeComponent(search.trim())}' : '/professionals'; final res = await _api.get(path); final list = (res is Map ? res['data'] : res) as List; diff --git a/lib/ui/views/professionals_view.dart b/lib/ui/views/professionals_view.dart index 2186416..b8491ec 100644 --- a/lib/ui/views/professionals_view.dart +++ b/lib/ui/views/professionals_view.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:prosapp_web_app/models/usuario_profesional.dart'; import 'package:prosapp_web_app/providers/professionals_provider.dart'; @@ -14,19 +15,35 @@ class ProfessionalsView extends StatefulWidget { class _ProfessionalsViewState extends State { String _search = ''; + Timer? _debounce; + final _controller = TextEditingController(); + + @override + void dispose() { + _debounce?.cancel(); + _controller.dispose(); + super.dispose(); + } + + void _onSearchChanged(String value) { + setState(() => _search = value); + _debounce?.cancel(); + _debounce = Timer(const Duration(milliseconds: 450), () { + context.read().getProfessionals(search: value); + }); + } + + void _clearSearch() { + _controller.clear(); + _onSearchChanged(''); + } @override Widget build(BuildContext context) { final provider = context.watch(); final isDark = context.watch().isDark; - final filtered = provider.professionals.where((p) { - if (_search.isEmpty) return true; - final q = _search.toLowerCase(); - return p.user.name.toLowerCase().contains(q) || - p.professionalInfo.profession.toLowerCase().contains(q) || - (p.user.city ?? '').toLowerCase().contains(q); - }).toList(); + final filtered = provider.professionals; final cardBg = isDark ? const Color(0xFF1E293B) : Colors.white; final border = isDark ? const Color(0xFF334155) : const Color(0xFFE5E7EB); @@ -39,7 +56,8 @@ class _ProfessionalsViewState extends State { Padding( padding: const EdgeInsets.fromLTRB(0, 0, 0, 14), child: TextField( - onChanged: (v) => setState(() => _search = v), + controller: _controller, + onChanged: _onSearchChanged, style: TextStyle( color: isDark ? Colors.white : const Color(0xFF111827), fontSize: 14, @@ -52,7 +70,7 @@ class _ProfessionalsViewState extends State { suffixIcon: _search.isNotEmpty ? IconButton( icon: Icon(Icons.close, size: 18, color: hintColor), - onPressed: () => setState(() => _search = ''), + onPressed: _clearSearch, ) : null, filled: true,