首页 > 其他分享 >flutter_weixin索引条实现

flutter_weixin索引条实现

时间:2024-04-04 17:59:09浏览次数:16  
标签:index getFirstWordPinyin weixin 0.0 side 索引 titlesList PinyinHelper flutter

 1、定义索引条

List<String> side

采用Stack,用Positioned将其位置调整到屏幕右侧

2、将姓名经行排序,使用PinyinHelper

titlesList.sort((String a, String b) {
      String aPinyin = PinyinHelper.getFirstWordPinyin(a).toUpperCase();
      String bPinyin = PinyinHelper.getFirstWordPinyin(b).toUpperCase();
      return aPinyin.compareTo(bPinyin); // 按照拼音首字母顺序比较
    });

3、将姓名按顺序展示到通讯录,每个字母的第一个姓名都有大写字母标识

if (PinyinHelper.getFirstWordPinyin(titlesList[index - 4])
                            .substring(0, 1)
                            .toUpperCase() ==
                        PinyinHelper.getFirstWordPinyin(titlesList[index - 5])
                            .substring(0, 1)
                            .toUpperCase()) {
                      return ContactPeople(titlesList[index - 4]);
                    } else {
                      return Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          SizedBox(
                            height: 30.0,
                            child: Text(
                                '  ${PinyinHelper.getFirstWordPinyin(titlesList[index - 4]).substring(0, 1).toUpperCase()}'),
                          ),
                          ContactPeople(titlesList[index - 4]),
                        ],
                      );
                    }

4、获得字母高度,然后计算出对应字母,返回对应字母

String getIndex(BuildContext context, Offset globalPosition){
    RenderObject? renderObject = context.findRenderObject();
    RenderBox? box;
    if (renderObject != null && renderObject is RenderBox) {
      box = renderObject as RenderBox; // 类型转换
    }
    double? y = box?.globalToLocal(globalPosition).dy;
    //计算高度单位
    var itemHeight = screenHeight(context) / 2 / widget.side.length;

    //定位
    // int index = ((y! - screenHeight(context) / 8) ~/ itemHeight)
    //     .clamp(0, widget.side.length - 1);
    int index = (y! ~/ itemHeight)
        .clamp(0, widget.side.length - 1);
    // print(side[index+2]);
    return widget.side[index];
  }

5、计算通讯录姓名字母的位置

if( i >= 1){
        if (PinyinHelper.getFirstWordPinyin(titlesList[i-1])
            .substring(0, 1)
            .toUpperCase() ==
            PinyinHelper.getFirstWordPinyin(titlesList[i])
                .substring(0, 1)
                .toUpperCase()) {
          groupOffset = groupOffset + 50.5;
        } else {
          groupOffset = groupOffset + 80.5;
          _groupOffsetMap.addAll({
            PinyinHelper.getFirstWordPinyin(titlesList[i])
                .substring(0, 1)
                .toUpperCase(): groupOffset
          });
        }
      }

6、利用返回的字母得到具体位置开始滑动

indexBarCallBack: (String str) {
                print('========${_groupOffsetMap[str]}====$str');
                _scrollController.animateTo(_groupOffsetMap[str]!,
                    duration: const Duration(microseconds: 100),
                    curve: Curves.easeIn);
              },

效果展示:

完整代码:

//ContactView.dart
import 'package:flutter/material.dart';
import 'package:pinyin/pinyin.dart';
import 'package:weixin/widget/IndexBar.dart';
import 'package:weixin/widget/contact_people.dart';

import 'other/line.dart';

class ContactView extends StatefulWidget {
  const ContactView({super.key});

  @override
  _ContactViewState createState() => _ContactViewState();
}

class _ContactViewState extends State {
  //微信索引条
  List<String> side = [
    '↑',
    '✫',
    'A',
    'B',
    'C',
    'D',
    'E',
    'F',
    'G',
    'H',
    'I',
    'J',
    'K',
    'L',
    'M',
    'N',
    'O',
    'P',
    'Q',
    'R',
    'S',
    'T',
    'U',
    'V',
    'W',
    'X',
    'Y',
    'Z',
    '#'
  ];

  List<Widget> words = [];

  List<String> head = ['新的朋友', '群聊', '标签', '公众号'];
  List<String> titlesList = [
    '刘备',
    '孙权',
    "诸葛亮",
    "赵云",
    "周瑜",
    "鲁肃",
    "司马懿",
    "袁绍",
    "华佗",
    "华雄",
    "公孙瓒",
    "刘表",
    "典韦",
    "黄忠",
    "刘禅",
    "徐庶",
    "郭嘉",
    "荀攸",
    '曹操',
  ];

  // final Map _groupOffsetMap = {
  //   // side[0]:0.0,
  // };
  late final Map<String, double> _groupOffsetMap;

  //各个字母联系人的高度
  final Map<String, double> _groupHeight = {
    'A': 0.0,
    'B': 0.0,
    'C': 0.0,
    'D': 0.0,
    'E': 0.0,
    'F': 0.0,
    'G': 0.0,
    'H': 0.0,
    'I': 0.0,
    'J': 0.0,
    'K': 0.0,
    'L': 0.0,
    'M': 0.0,
    'N': 0.0,
    'O': 0.0,
    'P': 0.0,
    'Q': 0.0,
    'R': 0.0,
    'S': 0.0,
    'T': 0.0,
    'U': 0.0,
    'V': 0.0,
    'W': 0.0,
    'X': 0.0,
    'Y': 0.0,
    'Z': 0.0,
    '#': 0.0,
  };

  late ScrollController _scrollController;

  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController();

    titlesList.sort((String a, String b) {
      String aPinyin = PinyinHelper.getFirstWordPinyin(a).toUpperCase();
      String bPinyin = PinyinHelper.getFirstWordPinyin(b).toUpperCase();
      return aPinyin.compareTo(bPinyin); // 按照拼音首字母顺序比较
    });

    for (int i = 0; i < side.length; i++) {
      words.add(Expanded(
          child: Text(side[i],
              style: const TextStyle(fontSize: 10, color: Colors.grey))));
    }

    _groupOffsetMap = {
      side[0]: 0.0,
      side[1]: 0.0,
    };
    double groupOffset = 50.5 * head.length;
    for (int i = 0; i < titlesList.length; i++) {
      // if (i == 2) {
      //   _groupOffsetMap.addAll({side[i]: groupOffset});
      //   //增加高度
      //   //groupOffset = groupOffset
      // }
      //
      if (i == 0) {
        _groupOffsetMap.addAll({'C': groupOffset});
      }
      if( i >= 1){
        if (PinyinHelper.getFirstWordPinyin(titlesList[i-1])
            .substring(0, 1)
            .toUpperCase() ==
            PinyinHelper.getFirstWordPinyin(titlesList[i])
                .substring(0, 1)
                .toUpperCase()) {
          groupOffset = groupOffset + 50.5;
        } else {
          groupOffset = groupOffset + 80.5;
          _groupOffsetMap.addAll({
            PinyinHelper.getFirstWordPinyin(titlesList[i])
                .substring(0, 1)
                .toUpperCase(): groupOffset
          });
        }
      }

      // }
    }
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
        appBar: AppBar(
          backgroundColor: const Color.fromARGB(0xff, 0xf2, 0xf2, 0xf2),
          elevation: 0.0,
          title: const Text("通讯录"),
        ),
        body: Stack(
          children: [
            Container(
              child: ListView.builder(
                  controller: _scrollController,
                  itemCount: titlesList.length + head.length,
                  itemBuilder: (BuildContext context, int index) {
                    if (index < head.length) {
                      return ContactPeople(head[index]);
                    }
                    if (index == head.length) {
                      return Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          const SizedBox(
                            height: 30.0,
                            child: Text('  C'),
                          ),
                          ContactPeople(titlesList[index - 4]),
                        ],
                      );
                    }
                    if (PinyinHelper.getFirstWordPinyin(titlesList[index - 4])
                            .substring(0, 1)
                            .toUpperCase() ==
                        PinyinHelper.getFirstWordPinyin(titlesList[index - 5])
                            .substring(0, 1)
                            .toUpperCase()) {
                      //长度加一
                      // _groupHeight[
                      //     PinyinHelper.getFirstWordPinyin(titlesList[index - 4])
                      //         .substring(0, 1)
                      //         .toUpperCase()] = 50.5 +
                      //     _groupHeight[PinyinHelper.getFirstWordPinyin(
                      //             titlesList[index - 4])
                      //         .substring(0, 1)
                      //         .toUpperCase()]!;
                      return ContactPeople(titlesList[index - 4]);
                    } else {
                      // 长度加一
                      // _groupHeight[
                      //     PinyinHelper.getFirstWordPinyin(titlesList[index - 4])
                      //         .substring(0, 1)
                      //         .toUpperCase()] = 50.5 +
                      //     _groupHeight[PinyinHelper.getFirstWordPinyin(
                      //             titlesList[index - 4])
                      //         .substring(0, 1)
                      //         .toUpperCase()]!;
                      return Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          SizedBox(
                            height: 30.0,
                            child: Text(
                                '  ${PinyinHelper.getFirstWordPinyin(titlesList[index - 4]).substring(0, 1).toUpperCase()}'),
                          ),
                          ContactPeople(titlesList[index - 4]),
                        ],
                      );
                    }
                  }),
            ),
            IndexBar(
              words,
              side,
              indexBarCallBack: (String str) {
                print('========${_groupOffsetMap[str]}====$str');
                _scrollController.animateTo(_groupOffsetMap[str]!,
                    duration: const Duration(microseconds: 100),
                    curve: Curves.easeIn);
              },
            ),
            //indexBarCallBack: (String str) {
          ],
        ));
  }
}
//contact_people.dart
import 'package:flutter/material.dart';

import '../other/line.dart';

class ContactPeople extends StatefulWidget {
  final String titlesList;
  const ContactPeople(this.titlesList, {Key? key}) : super(key: key);


  @override
  State<ContactPeople> createState() => _ContactPeopleState();
}

class _ContactPeopleState extends State<ContactPeople> {

  // List<Map<String, dynamic>> dataList = [
  //   {
  //     'title': '刘备',
  //     'image': 'images/刘备.png',
  //   },
  //   {
  //     'title': '刘备',
  //     'image': 'images/刘备.png',
  //   },
  //   {
  //     'title': '刘备',
  //     'image': 'images/刘备.png',
  //   },
  // ];

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Container(
          height: 50.0,
          color: Colors.white,
          child: ListTile(
            title: Text(widget.titlesList),
            leading: Image.asset(
              "images/${widget.titlesList}.png",
              width: 35.0,
              height: 35.0,
              fit: BoxFit.cover,
            ),
          ),
        ),
        const line(),
      ],
    );
  }
}
//IndexBar.dart
import 'package:flutter/material.dart';
class IndexBar extends StatefulWidget {
  final void Function(String str) indexBarCallBack;
  final List<Widget> words;
  final List<String> side;

  const IndexBar(this.words, this.side, {Key? key, required this.indexBarCallBack}) : super(key: key);

  @override
  State<IndexBar> createState() => _IndexBarState();
}

class _IndexBarState extends State<IndexBar> {


  double screenHeight(BuildContext context) {
    // 使用MediaQuery获取屏幕的高度
    return MediaQuery.of(context).size.height;
  }

  String getIndex(BuildContext context, Offset globalPosition){
    RenderObject? renderObject = context.findRenderObject();
    RenderBox? box;
    if (renderObject != null && renderObject is RenderBox) {
      box = renderObject as RenderBox; // 类型转换
    }
    double? y = box?.globalToLocal(globalPosition).dy;
    //计算高度单位
    var itemHeight = screenHeight(context) / 2 / widget.side.length;

    //定位
    // int index = ((y! - screenHeight(context) / 8) ~/ itemHeight)
    //     .clamp(0, widget.side.length - 1);
    int index = (y! ~/ itemHeight)
        .clamp(0, widget.side.length - 1);
    // print(side[index+2]);
    return widget.side[index];
  }

  @override
  Widget build(BuildContext context) {
    return Positioned(
        right: 0.0,
        top: screenHeight(context) / 8,
        height: screenHeight(context) / 2,
        width: 30,
        child: GestureDetector(
          onVerticalDragUpdate: (DragUpdateDetails details) {
            widget.indexBarCallBack(
                getIndex(context, details.globalPosition));
          },
          onVerticalDragDown: (DragDownDetails details) {
            widget.indexBarCallBack(getIndex(context, details.globalPosition));
          },
          child: Container(
            // color: Colors.grey,
            child: Column(
              children: widget.words,
            ),
          ),
        ));
  }
}

标签:index,getFirstWordPinyin,weixin,0.0,side,索引,titlesList,PinyinHelper,flutter
From: https://blog.csdn.net/scan001111/article/details/137377108

相关文章

  • Java面试题:简述数据库性能优化的常见手段,如索引优化、SQL语句优化等。
    数据库性能优化是确保数据库系统高效运行的关键步骤。以下是一些常见的数据库性能优化手段:1.索引优化:创建索引:为经常用于查询条件的字段创建索引,可以大大加快查询速度。避免过多索引:虽然索引可以加快查询,但过多索引会减慢写操作,并占用额外空间。使用复合索引:当查询条件包......
  • 搜索引擎-03-搜索引擎原理
    拓展阅读搜索引擎-01-概览搜索引擎-02-分词与全文索引搜索引擎-03-搜索引擎原理Crawlhtmlunit模拟浏览器动态js爬虫入门使用简介Crawljsoup爬虫使用jsoup无法抓取动态js生成的内容CrawlWebMagic爬虫入门使用简介webmagic全网搜索引擎架构与流程如何?全网搜索......
  • MySQL索引背后的数据结构及算法原理
    摘要本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题。特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BTree索引,哈希索引,全文索引等等。为了避免混乱,本文将只关注于BTree索引,因为这是平常使用MyS......
  • KingbaseES 为什么select主键列不走索引
    背景有客户提出一个问题。一个类似这样的SQL语句,selectcount(id)from为什么执行计划用全表扫,不用索引。id列上有主键。分析test=#explain(analyze,buffers)selectcount(id)fromt1;QUERYPLAN---------------......
  • KingbaseES 数据库创建索引慢的可能原因
    1.表大小如果表太大,数据很多,索引创建的时候,会导致创建索引的时间很慢。如果表很大,可以考虑重新设计表结构或拆分表。还可以考虑使用分区表,使子分区的数据减少,创建分区表也可以使索引变小,增加索引创建速度,有助于查询效率。2.索引类型不同类型的索引建立的速度可能会有所不同,因......
  • openGauss 全文索引
    全文索引可获得性本特性自openGauss1.1.0版本开始引入。特性简介openGauss中提供的全文索引功能可以对文档进行预处理,并且可以使后续的搜索更快速。客户价值openGauss全文索引功能提供了查询可读性文档的能力,并且通过查询相关度将结果进行排序。特性描述构建全文索引的......
  • flutter随笔
    dart语法list//创建指定长度listList.generate(6,(index)=>CircleButton(index:index));基础视图结构StatelessWidget与StatefulWidget区别StatelessWidget:无状态的widgets是不可变的,这意味着它们的属性不能改变,所有的值都是finalStatefulWidget:有状态的wid......
  • 搜索引擎-01-概览
    拓展阅读搜索引擎-01-概览搜索引擎-02-分词与全文索引搜索引擎-03-搜索引擎原理Crawlhtmlunit模拟浏览器动态js爬虫入门使用简介Crawljsoup爬虫使用jsoup无法抓取动态js生成的内容CrawlWebMagic爬虫入门使用简介webmagic详细介绍一下搜索引擎搜索引擎是一种......
  • 第10章_索引优化与查询优化
    都有哪些维度可以进行数据库调优?简言之:索引失效、没有充分利用到索引——建立索引关联查询太多JOIN(设计缺陷或不得已的需求)——SQL优化服务器调优及各个参数设置(缓冲、线程数等)——调整my.cnf数据过多——分库分表关于数据库调优的知识非常分散。不同的DBMS,不同的公司,不同......
  • Spring Data Elasticsearch String类型不指定filed 索引创建情况
    对于String类型的字段如果不指定类型会默认创建两种倒排索引       "itemSkuCodes":{         "type":"text",         "fields":{           "keyword":{             "type":"keyword",           ......