1.列表索引
效果图:
indexer.dart
Dart
import 'package:json_annotation/json_annotation.dart';
abstract class Indexer {
///用于排序的字母
@JsonKey(includeFromJson: false, includeToJson: false)
String? sortLetter;
///用于排序的拼音
@JsonKey(includeFromJson: false, includeToJson: false)
String? fullLetter;
///用于继承类设置需要索引的字段
String? getFullName();
}
indexer_util.dart
Dart
import 'package:kq_flutter_widgets/utils/ex/string_ex.dart';
import 'package:lpinyin/lpinyin.dart';
import 'indexer.dart';
class IndexerUtil {
///始终排在列表最顶端,可自己改为特定值
static String ALWAYS_TOP_SYMBOL = "★";
///始终排在列表最底部,可自己改为特定值
static String ALWAYS_BOTTOM_SYMBOL = "#";
///给数据排序,默认根据{@link IndexModel#getSortLetter()}内容升序排列,其中有两个特殊字符{@link #ALWAYS_TOP_SYMBOL}
///和{@link #ALWAYS_BOTTOM_SYMBOL}。如果{@link IndexModel#getSortLetter()}获取内容为{@link #ALWAYS_TOP_SYMBOL},
///则始终排在最顶端,如果{@link IndexModel#getSortLetter()}获取内容为{@link #ALWAYS_BOTTOM_SYMBOL},则始终排在最低端。
///通常情况下都无需调用{@link IndexModel#setSortLetter(String)},除非你想干预某个数据在列表的排序结果
/// @param isIndexNumber 是否索引数字,如果不索引,则全部放到{@linkplain #ALWAYS_BOTTOM_SYMBOL #}号分组
/// {@code true} 索引
/// {@code false} 不索引
/// @param isUserName 是否是人名,{@code true} 处理多音字作为姓氏时的正确读音
static List<String> getIndexData<T extends Indexer>(
{required List<T> data,
LatterType type = LatterType.lowerCase,
bool isIndexNumber = false}) {
for (T t in data) {
_swap(t, isIndexNumber, type);
}
_sortData(data);
///TODO 优化,从sectionData直接过去key
List<String> indexData = [];
for (T t in data) {
if (t.sortLetter == null && !indexData.contains(ALWAYS_BOTTOM_SYMBOL)) {
indexData.add(ALWAYS_BOTTOM_SYMBOL);
} else if (!indexData.contains(t.sortLetter)) {
indexData.add(t.sortLetter!);
}
}
return indexData;
}
///给数据排序,默认根据{@link IndexModel#getSortLetter()}内容升序排列,其中有两个特殊字符{@link #ALWAYS_TOP_SYMBOL}
///和{@link #ALWAYS_BOTTOM_SYMBOL}。如果{@link IndexModel#getSortLetter()}获取内容为{@link #ALWAYS_TOP_SYMBOL},
///则始终排在最顶端,如果{@link IndexModel#getSortLetter()}获取内容为{@link #ALWAYS_BOTTOM_SYMBOL},则始终排在最低端。
///通常情况下都无需调用{@link IndexModel#setSortLetter(String)},除非你想干预某个数据在列表的排序结果
/// @param isIndexNumber 是否索引数字,如果不索引,则全部放到{@linkplain #ALWAYS_BOTTOM_SYMBOL #}号分组
/// {@code true} 索引
/// {@code false} 不索引
/// @param isUserName 是否是人名,{@code true} 处理多音字作为姓氏时的正确读音
static Map<String, List<T>> getSectionData<T extends Indexer>(
{required List<T> data,
LatterType type = LatterType.lowerCase,
bool isIndexNumber = false}) {
for (T t in data) {
_swap(t, isIndexNumber, type);
}
_sortData(data);
Map<String, List<T>> sectionData = {};
List<String>? indexData = [];
for (T t in data) {
if (t.sortLetter == null && !indexData.contains(ALWAYS_BOTTOM_SYMBOL)) {
indexData.add(ALWAYS_BOTTOM_SYMBOL);
sectionData.putIfAbsent(ALWAYS_BOTTOM_SYMBOL, () => [t]);
} else if (!indexData.contains(t.sortLetter)) {
indexData.add(t.sortLetter!);
sectionData.putIfAbsent(t.sortLetter!, () => [t]);
} else {
sectionData.update(t.sortLetter!, (value) {
value.add(t);
return value;
});
}
}
return sectionData;
}
///排序
static _sortData<T extends Indexer>(List<T> data) {
data.sort((left, right) {
if (left.sortLetter == ALWAYS_TOP_SYMBOL) {
return -1;
} else if (left.sortLetter != ALWAYS_TOP_SYMBOL &&
right.sortLetter == ALWAYS_TOP_SYMBOL) {
return 1;
} else if (left.sortLetter == ALWAYS_BOTTOM_SYMBOL &&
right.sortLetter != ALWAYS_BOTTOM_SYMBOL) {
return 1;
} else if (right.sortLetter == ALWAYS_BOTTOM_SYMBOL) {
return -1;
} else {
return (left.sortLetter ?? "").compareTo(right.sortLetter ?? "");
}
});
}
///获取汉字全拼首字母
static String? _getSelling<T extends Indexer>(T t) {
String? latter = t.getFullName();
if (latter.isNullOrEmpty) {
return null;
}
return PinyinHelper.getFirstWordPinyin(latter!);
}
///根据全拼获取首字母
static String _getFirstLetter(
String pinyin, bool isIndexNumber, LatterType type) {
if (pinyin.isNullOrEmpty) {
return ALWAYS_BOTTOM_SYMBOL;
}
String sortString = pinyin.substring(0, 1);
if (RegExp(r"[A-Za-z]").hasMatch(sortString)) {
return type == LatterType.upperCase
? sortString.toUpperCase()
: sortString.toLowerCase();
} else if (isIndexNumber && RegExp(r"\d").hasMatch(sortString)) {
return sortString;
} else {
return ALWAYS_BOTTOM_SYMBOL;
}
}
///数据转化
static _swap<T extends Indexer>(T t, bool isIndexNumber, LatterType type) {
t.fullLetter ??= _getSelling(t);
t.sortLetter ??= _getFirstLetter(t.fullLetter!, isIndexNumber, type);
if (t.sortLetter != null && t.sortLetter!.isEmpty) {
t.sortLetter = ALWAYS_BOTTOM_SYMBOL;
}
}
}
enum LatterType {
///大写
upperCase,
///小写
lowerCase
}
上面的只是帮助类,帮助数据分组和索引,界面的分组和展示需要自己布局实现。
2.搜索帮助
search_able.dart
Dart
abstract class SearchAble{
String toSearch();
}
search_util.dart
Dart
import 'package:kq_flutter_widgets/utils/ex/string_ex.dart';
import 'package:kq_flutter_widgets/utils/search/search_able.dart';
/// 搜索帮助类
class SearchUtil {
/// 根据搜索内容搜索出相关数据
/// 用户需要搜索的数据类需要继承[SearchAble],并实现[toSearch]方法
static List<T> search<T extends SearchAble>(String? searchText, List<T> data) {
List<T> searchData = [];
if (searchText.isNotNullOrEmpty) {
for (T t in data) {
if (t.toSearch().isNotNullOrEmpty &&
t.toSearch().toLowerCase().trim().contains(searchText!.toLowerCase())) {
searchData.add(t);
}
}
}
return searchData;
}
}