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,

            ),

          ),

        ));

  }

}


评论

此博客中的热门博文

登录界面实现