/// Letters of an A–Z index; names that don't start with a letter go under `#`. final indexLetters = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''), '#']; const _accented = 'ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝŸ'; const _unaccented = 'AAAAAACEEEEIIIINOOOOOUUUUYY'; /// Uppercase without accents, so "Émile" sorts and indexes under E. String _fold(String name) { return [ for (final char in name.trim().toUpperCase().split('')) switch (_accented.indexOf(char)) { -1 => char, final index => _unaccented[index], }, ].join(); } String indexLetterOf(String name) { final folded = _fold(name); if (folded.isEmpty) return '#'; final first = folded[0]; return indexLetters.contains(first) ? first : '#'; } /// A–Z regardless of case and accents, with the `#` names last. int compareByIndex(String a, String b) { final byLetter = indexLetters .indexOf(indexLetterOf(a)) .compareTo(indexLetters.indexOf(indexLetterOf(b))); if (byLetter != 0) return byLetter; final byName = _fold(a).compareTo(_fold(b)); return byName != 0 ? byName : a.compareTo(b); }