Flutter实用工具Indexer列表索引和Search搜索帮助。

18 篇文章 0 订阅

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<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

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<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;
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许天成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值