/// Lightweight fuzzy matching for product search. library; String _normalize(String value) => value.trim().toLowerCase().replaceAll(RegExp(r'\s+'), ' '); /// True when [query] fuzzily matches [text]. bool fuzzyMatch(String query, String text) { final q = _normalize(query); if (q.isEmpty) return true; final t = _normalize(text); if (t.contains(q)) return true; for (final word in q.split(' ')) { if (word.isEmpty) continue; if (!t.contains(word) && !_subsequence(word, t)) return false; } if (q.contains(' ')) return true; return _subsequence(q, t); } /// Higher is better. Null when [query] does not match. int? fuzzyScore(String query, String text) { final q = _normalize(query); if (q.isEmpty) return 0; final t = _normalize(text); if (t == q) return 1000; if (t.startsWith(q)) return 800; if (t.contains(q)) return 600 - t.indexOf(q); if (!fuzzyMatch(q, t)) return null; return 200 - (t.length - q.length).abs().clamp(0, 150); } bool _subsequence(String query, String text) { var i = 0; for (var c = 0; c < text.length && i < query.length; c++) { if (text.codeUnitAt(c) == query.codeUnitAt(i)) i++; } return i == query.length; }