IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> flutter 通讯录 -> 正文阅读

[移动开发]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)
              ),
            ),
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-05-18 17:46:02  更:2022-05-18 17:47:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 1:33:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码