likes
comments
collection
share

Flutter学习-17- 微信项目学习-索引条联动

作者站长头像
站长
· 阅读数 3
  • 我们上一篇实现了索引条的显示和点击状态变化,接下来我们要实现索引条的气泡展示和通讯录的联动。

1. 计算出点击或拖拽的文字

我们思路是通过计算出索引条的每一个字母所在的位置返回对应的字母展示出来。 我们通过上下文拿到当前小部件的盒子,RenderBox相当于索引条的大小,我们通过globalToLocal把外部的坐标转换当前控件的坐标系统。类似我们把子视图的点由在全局的坐标点转换为父视图的坐标,我们就不用转换了。

onVerticalDragUpdate: (DragUpdateDetails details){
  setState(() {
    RenderBox box = context.findRenderObject() as RenderBox ;
    print('$box.globalToLocal(details.localPosition).dy');
    print('手势移动的位置:$details');

  });
},

Flutter学习-17- 微信项目学习-索引条联动

拿到y值,globalToLocal当前位置我部件的原点(小部件左上角)的距离(x,y)。通过每个字符的高度,根据偏移

//拖拽
onVerticalDragUpdate: (DragUpdateDetails details){
  setState(() {
    RenderBox box = context.findRenderObject() as RenderBox ;
    double y =  box.globalToLocal(details.localPosition).dy;
    //item高度
    var itemHeight = screenHeight(context)/2 / INDEX_WORDS.length;
    int index = y ~/ itemHeight;
    print('选中了第$index个');

  });
},

Flutter学习-17- 微信项目学习-索引条联动

这里如果超出了屏幕数组也会报错

Flutter学习-17- 微信项目学习-索引条联动

我们也要设定下index的取值范围 我们使用clamp设定范围为0到INDEX_WORDS.length - 1得到对应的下标,最后在取出数组中的str。我们把方法抽出来

//获取选中的Item的字符!!
String getIndex(BuildContext context, Offset globalPosition) {
  //拿到点前小部件的盒子
  RenderBox box = context.findRenderObject() as RenderBox ;
  //拿到y值,globalToLocal当前位置我部件的原点(小部件左上角)的距离(x,y)
  double y = box.globalToLocal(globalPosition).dy;
  //算出字符高度
  var itemHeight = screenHeight(context) / 2 / INDEX_WORDS.length;
  //算出第几个item
  int index = (y ~/ itemHeight).clamp(0, INDEX_WORDS.length - 1);
  return INDEX_WORDS[index];
}

Flutter学习-17- 微信项目学习-索引条联动

2. 索引条的回调

我们在索引条中回调点击的返回的字符,我们需要高速外界的ListView,所以我们要一个回调,我们定义一个回调方法。

class CloumnIndexBar extends StatefulWidget {

  final void Function(String str) indexBarCallBack;
  CloumnIndexBar({required this.indexBarCallBack});

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

我们在点击和移动的时候把这个字符传递出去

//拖拽
onVerticalDragUpdate: (DragUpdateDetails details){
  setState(() {
  var str =  getIndex(context, details.globalPosition);
  widget.indexBarCallBack(str);
  print(str);

  });
},
//点击
onVerticalDragDown: (DragDownDetails details){
  setState(() {
    var str =  getIndex(context, details.globalPosition);
    widget.indexBarCallBack(str);
    print('手势进入的位置:$details');
    _backgroundColor = Color.fromRGBO(1, 1, 1, 0.5);
    _textColor = Colors.white;
  });

},

我们在创建的时候得到回调

Flutter学习-17- 微信项目学习-索引条联动

listView滚动需要控制器,所以我们需要开始的时候进行创建,这里直接定义

final ScrollController _scrollController =  ScrollController();

我们使用_scrollControlleranimateTo进行滚动,这里先写个300测试下

child:Stack(
  children: [
    ListView.builder(itemBuilder: _itemBuilder,
      itemCount: _headerData.length+_listDatas.length,
      controller: _scrollController,

    ),
    CloumnIndexBar(indexBarCallBack: (String str){

      _scrollController.animateTo(300, duration: Duration(microseconds: 100), curve: Curves.easeIn);

      print('选中了$str');
    },)
  ],
)

Flutter学习-17- 微信项目学习-索引条联动

因此我们可以算出来我们字母对应的位置。

3. 计算对应的偏移量

我们之前定义的cell的高度和头部的高度

double _cellHeight = 54.5;
double _groupHeight = 30.0;

之后我们定义一个字典,字典中每个字母存放对应的偏移量


final Map _groupOffsetMap = {
  INDEX_WORDS[0]: 0.0,
  INDEX_WORDS[1]: 0.0,
};

这里前2个是固定的,所以不用偏移。我们在计算的时候不能放到itembulid里面,滑动的时候才会加载,所以我们在initState中数据处理完成后进行计算。

var _groupOffset = _cellHeight * _headerData.length;//默认位置
//进过循环计算,将每一个头的位置算出来。放入字典
for (int i = 0; i < _listDatas.length; i++) {
  if (i < 1) {
    //第一个cell一定有头!
    _groupOffsetMap.addAll({_listDatas[i].indexLetter: _groupOffset});
    //保存完了再加_groupOffset
    _groupOffset += _cellHeight + _groupHeight;
  } else if (_listDatas[i].indexLetter == _listDatas[i - 1].indexLetter) {
    //不同存,只需要加Cell的高度
    _groupOffset += _cellHeight;
  } else {
    _groupOffsetMap.addAll({_listDatas[i].indexLetter: _groupOffset});
    //保存完了再加_groupOffset
    _groupOffset += _cellHeight + _groupHeight;
  }
}

我们在之前的回调中显示,为了防止不存在的我们加个判断

if (_groupOffsetMap[str] != null) {
  _scrollController.animateTo(_groupOffsetMap[str],
      duration: Duration(microseconds: 100),
      curve: Curves.easeIn);
}
print('选中了$str');

Flutter学习-17- 微信项目学习-索引条联动

4. 指示器显示

指示器左边的气泡我们可以把它和指示器当成一个整体,那么我们可以使用row进行布局。

Flutter学习-17- 微信项目学习-索引条联动 我们调整下索引条的宽度和整体的宽度,这样就居中

Flutter学习-17- 微信项目学习-索引条联动 我们继续设置下左边的指示器,主要是气泡和文字那么我们可以使用stack进行包裹

Flutter学习-17- 微信项目学习-索引条联动 我们调整下位置,居中

Flutter学习-17- 微信项目学习-索引条联动 我们知道flutter中的坐标是1到-1,中间是0,那么我们可以使用alignment来表示范围

Flutter学习-17- 微信项目学习-索引条联动 这里还要往下一点应该是y的范围(-1.1,1.1)

Flutter学习-17- 微信项目学习-索引条联动 我们定义3个变量,根据index算出-1.1在1.1的位置

double _indexBarOffsetY = 0.0; // -1.1到1.1之间的偏移量  中心点的是0
bool _indexBarHidden = true; // 是否隐藏
String _indexBarText = 'A'; // 当前正在显示的字母

我们点击或者拖拽的时候显示的字母判断是否隐藏 我们把这个之前返回的字符串改为index

Flutter学习-17- 微信项目学习-索引条联动

Widget build(BuildContext context) {
  final List<Widget> _words = [];

  for(int i = 0 ;i<INDEX_WORDS.length;i++){

    _words.add(Expanded(child: Text(INDEX_WORDS[i],style: TextStyle(fontSize: 10,color: _textColor),)));

  }
  return Positioned(
    top: screenHeight(context)/8,
    width: 120,
    right: 0,
    height: screenHeight(context)/2,
    child:
     Container(
       child:
       Row(
         children: [
           Container(

             alignment: Alignment(0,_indicatorOffsetY),
             width: 100,

             child: _indicatorHidden ? null : Stack(
               alignment: Alignment(-0.2,0),
               children: [
                 Image(
                   image: AssetImage('images/气泡.png'),
                   width: 60,
                 ),
                 Text(_indicatorText,
                   style: TextStyle(fontSize: 35, color: Colors.white),
                 )
               ],
             ),
           ),
           GestureDetector(
             //拖拽
             onVerticalDragUpdate: (DragUpdateDetails details){
               setState(() {
                 int  index  =  getIndex(context, details.globalPosition);
                 widget.indexBarCallBack(INDEX_WORDS[index]);
                 setState(() {
                   _indicatorOffsetY = 2.2 / INDEX_WORDS.length * index - 1.1;
                   _indicatorText = INDEX_WORDS[index];
                   _indicatorHidden = false;
                 });

               });
             },
             //点击
             onVerticalDragDown: (DragDownDetails details){
               setState(() {
                 int  index  =  getIndex(context, details.globalPosition);
                 widget.indexBarCallBack(INDEX_WORDS[index]);
                 print('手势进入的位置:$details');

                 _indicatorOffsetY = 2.2 / INDEX_WORDS.length * index - 1.1;
                 _indicatorText = INDEX_WORDS[index];
                 _indicatorHidden = false;
                 _backgroundColor = Color.fromRGBO(1, 1, 1, 0.5);
                 _textColor = Colors.white;
               });

             },
             //离开
             onVerticalDragEnd:(DragEndDetails details){
               setState(() {
                 _backgroundColor = Color.fromRGBO(1, 1, 1, 0);
                 _textColor = Colors.black;
                 _indicatorHidden = true;

               });

             },
             child:
             Container(
               width: 20,
               color:_backgroundColor,
               child:
               Column(
                 children: _words,
               ),
             ),
           )
         ],
       ),
     )
  );
}

最终的效果:

Flutter学习-17- 微信项目学习-索引条联动

转载自:https://juejin.cn/post/7030666250095165470
评论
请登录