1.列表索引
效果图:

indexer.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
- 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
extends Indexer>( - {required List
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
> getSectionDataextends Indexer>( - {required List
data, - LatterType type = LatterType.lowerCase,
- bool isIndexNumber = false}) {
- for (T t in data) {
- _swap(t, isIndexNumber, type);
- }
- _sortData(data);
-
- Map<String, List
> 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
extends Indexer>(List 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
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
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
- abstract class SearchAble{
- String toSearch();
- }
search_util.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
searchextends SearchAble>(String? searchText, List data) { - List
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;
- }
- }