flutter 通讯录

flutter 通讯录

工程仓库

国内的话需要梯子打开

https://www.dropbox.com/sh/u9s38957amql3yc/AABc3ncZd8L2gMtMAy83FKL0a?dl=0

如果不能下载的话请私信我

样式长这样

请添加图片描述

请添加图片描述

需求分析:

1、滑动会影响到右边的导航栏。

2、右边的导航栏具有点击和拖动的手势,位置点到的位置会影响到左边的list。

3、当list的导航栏的索引没有对应的list会选择最近的list。

4、当最后的list不占满的话,选倒数第二个list,若倒数第二个也不占满,以此类推上一个list。

需求没有说做停留索引,所以我没做了。

整体做法

页面

页面的话整体是个stack

占整个的是一个singleChildScrollview,分两部分,一部分是头Container,下面那部分是ListView嵌套ListView:目的是为了数据好处理。

嵌套一个导航栏:整体是Row左边是指示器,位置是根据右边的选择来做判定,右边是一个嵌套了GestureDetectorListViewGestureDetector具有点击、滑动、取消点击的回调。

数据

创建一个长度为27的数组来存储高度(# + A~Z)。数据可能 B没有,那该位置就是上一个A的位置。当滑动的时候,根据offset来判断当前的位置indexLocation为右边的导航栏显示更新。
请添加图片描述

核心代码

整体的scrollview + 模拟数据
  // 存储导航列表的高度位置瞄点
  List<double> scrollHeightList = [];
  // item高度
  double itemHeight = 60;
  // 导航item高度
  double sessionHeight = 38.5;
  // 当前选择item的位置
  int indexLocation = -1;
  // 最后占满屏幕的高度
  double lastHeight = 0;
  // 最后占满屏幕的位置
  int lastIndex = 0;
  // 标签页高度
  double firstItemHeight = 0;
  List<TeacherChooseList> teacherList = [];
  // 数据选择的字典
  Map chooseIndex = {'first': -1, 'second': -1};

在数据的创建时就要把高度给存在scrollHeightList

 void createData() {
    List abcList = ['#','A','B','C','D','E','F'];
    abcList.forEach((element) {
      List<TeacherChooseModel> teacherItem = [];
      for(int i = 0; i < 5; i++) {
        TeacherChooseModel model = TeacherChooseModel('$element $i 中文 yingwen', false, '');
        teacherItem.add(model);
      }
      final TeacherChooseModel model = TeacherChooseModel('$element ddd', false, '一年级1班101');
      teacherItem.add(model);
      final TeacherChooseList chooseList = TeacherChooseList(element, teacherItem);
      teacherList.add(chooseList);
    });
    List<TeacherChooseList> dataList = teacherList;
    double textWidth = '文本文本文本文本文本文本文本文本文本文本'.paintWidthWithTextStyle(
        const TextStyle(
          fontSize: 12
        ));
    double topItemHeight = 0;
    if(textWidth > (MediaQuery.of(widget.context).size.width - 30)) {
      topItemHeight = 33.5;
    }else {
      topItemHeight = 17;
    }
    firstItemHeight = topItemHeight + 52.5;
    double totalHeight = firstItemHeight;
    scrollHeightList.add(totalHeight);
    int wordIndex = 0;
    lastHeight += sessionHeight +
        itemHeight * dataList[dataList.length - 1].teacherItem.length;
    for(int i = 0; i < indexWord.length; i++) {

      for(int j = 1; j < dataList.length; j++) {

        if(dataList[j - 1].index == indexWord[i]) {
          if (j + 1 == (dataList.length)) {
            int dataIndex = j;
            print(MediaQuery.of(widget.context).size.height
                - firstItemHeight
                - MediaQuery.of(widget.context).padding.top
                - 64);
            while (lastHeight < (MediaQuery.of(widget.context).size.height
                - firstItemHeight
                - MediaQuery.of(widget.context).padding.top
                - 64)) {
              if(dataIndex >= 0) {
                lastHeight += sessionHeight +
                    itemHeight * dataList[dataIndex].teacherItem.length;
              }
              dataIndex -= 1;
              if(lastIndex == 0) {
                lastIndex = wordIndex;
              }
            }

          }

            totalHeight += sessionHeight + itemHeight*dataList[j - 1].teacherItem.length;
            break;
          // }
        }

      }
      scrollHeightList.add(totalHeight);
      wordIndex += 1;
    }
  }

scrollview的滑动监听

  void _scrollListener() {
    final double offsetY = _scrollController.offset;

    setState(() {
      for(int i = 0; i < scrollHeightList.length; i ++) {
        if(offsetY < scrollHeightList[i]) {
          indexLocation = i - 1;
          break;
        }else {
          for (int i = 0; i < indexWord.length; i++) {
            if(indexWord[i] == teacherList.last.index) {
              indexLocation = i;
              break;
            }
          }
        }
      }
    });
  }

导航栏

需要传递的数据

List indexWord = ['#','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'];
class TeacherChooseIndex extends StatefulWidget {
  final void Function(int index) indexBarCallBack;
  double totalHeight;
  int chooseIndex = -1;
  double firstItemHeight;
  TeacherChooseIndex({this.indexBarCallBack, this.totalHeight, this.chooseIndex, this.firstItemHeight});
  @override
  TeacherChooseIndexState createState() => TeacherChooseIndexState();
}

内部的数据

class TeacherChooseIndexState extends State<TeacherChooseIndex> {
  double _totalHeight = 0;
  double _indicatorY = 0.0;
  bool _indicatorHidden = true;
  String _indicatorText = 'A';
  double dragHeight = 0;
  double _firstItemHeight = 0;
  @override
  void initState() {
    super.initState();
    _totalHeight = widget.totalHeight;
    dragHeight = 25*_totalHeight/27;
    _firstItemHeight = widget.firstItemHeight;
  }

滑动手势获取当前的index位置

  int getIndexItem(BuildContext context,Offset globalPosition){
    //拿到当前盒子
    final RenderBox box = context.findRenderObject() as RenderBox;
    //拿到y值,当前位置到部件原点(部件左上角)的距离(x,y)
    var y = box.globalToLocal(globalPosition).dy;
    //算出字符高度
    final itemHeight = dragHeight/27;
    int index = y ~/itemHeight.clamp(0, indexWord.length-1) - 1;//~取整,设置取整范围clamp
    if(index > 26) {
      index = 26;
    }
    if (index < 0) {
      index = 0;
    }
    print('${indexWord[index]}');
    return index;
  }

手势的操作

GestureDetector(
              onVerticalDragDown: (DragDownDetails details){
                final int index = getIndexItem(context, details.globalPosition);
                widget.indexBarCallBack(index);
                setState(() {
                  widget.chooseIndex = index;
                  _indicatorY =  index * dragHeight/27;
                  _indicatorText = indexWord[index];
                  _indicatorHidden = false;//是否隐藏指示器
                });
              },
              onVerticalDragCancel: () {
                setState(() {
                  _indicatorHidden = true; //是否隐藏指示器
                });
              },
              onVerticalDragEnd:(DragEndDetails details){
                setState(() {
                  _indicatorHidden = true;//是否隐藏指示器
                });
              },
              onVerticalDragUpdate: (DragUpdateDetails details){
                final int index = getIndexItem(context, details.globalPosition);
                widget.indexBarCallBack(index);
                setState(() {
                  widget.chooseIndex = index;
                  _indicatorY =  index * dragHeight/27;
                  _indicatorText = indexWord[index];
                  _indicatorHidden = false;//是否隐藏指示器
                });
              },
              child: Container(
                width:dragHeight/27,
                // color: AppColor.black,
                child: _getListView(context)
              ),
            ),
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值