flutter_weixin索引条实现

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

  • 9
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值