字母索引快速定位
关于Context的获取
将Widegt写到方法体中与单独写成一个Wiget的区别在于能否拿到context
///右栏的字母表
Widget _buildLetterIndexView(BuildContext context) => Positioned(
// top: getScreenUtilHeight(55),
right: 0,
bottom: 75,
width: ScreenUtil().setHeight(30),
child: Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.symmetric(vertical: ScreenUtil().setHeight(5)),
color: Colors.transparent,
// color: Colors.amberAccent,
child: Column(
children: _presenter
.getIndexTags()
.map((letter) => GestureDetector(
onTapDown: (details) {
setState(() {
_onTap = letter;
jumpToLetter(letter);
});
},
onTapUp: (details) {
setState(() {
_onTap = '';
});
},
onLongPressStart: (details) {
setState(() {
_onTap = letter;
});
jumpToLetter(letter);
},
onLongPressEnd: (details) {
setState(() {
_onTap = '';
});
},
onVerticalDragStart: (start) {
//获取滑动点击的屏幕坐标
//获取点触摸到的标签的父盒子,indexBar
final RenderBox rb = context.findRenderObject();
//计算触摸点在父盒子中的相对位置
var local = rb.globalToLocal(start.globalPosition);
//获取单个标签的高度
final double hight =
rb.size.height / _presenter.getIndexTags().length;
print('hight:$hight');
//求当前触摸的标签的index
final int index =
(local.dy / _presenter.getIndexTags().length).round();
print('当前的index:$index');
setState(() {
if (index <= 0) {
_onTap = _presenter.getIndexTags().first;
} else if (index >= _presenter.getIndexTags().length) {
_onTap = _presenter.getIndexTags().last;
} else {
_onTap = _presenter.getIndexTags()[index];
}
});
jumpToLetter(_onTap);
},
onVerticalDragUpdate: (details) {
//获取滑动点击的屏幕坐标
//获取点触摸到的标签的父盒子,indexBar
final RenderBox rb = context.findRenderObject();
//计算触摸点在父盒子中的相对位置
print('position:${details.globalPosition}');
print('position:${details.localPosition}');
var local = rb.globalToLocal(details.globalPosition);
print('position:$local');
//获取单个标签的高度
final double hight =
rb.size.height / _presenter.getIndexTags().length;
print('hight:$hight');
//求当前触摸的标签的index
final int index = (local.dy / hight).round();
print('当前的index:$index');
setState(() {
if (index <= 0) {
_onTap = _presenter.getIndexTags().first;
} else if (index >= _presenter.getIndexTags().length) {
_onTap = _presenter.getIndexTags().last;
} else {
_onTap = _presenter.getIndexTags()[index];
}
});
jumpToLetter(_onTap);
},
onVerticalDragEnd: (details) {},
child: Padding(
padding: EdgeInsets.only(
top: ScreenUtil().setHeight(1),
bottom: ScreenUtil().setHeight(1)),
child: Container(
// color: Colors.blueAccent,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(80),
color: _onTap == letter
? Colours.black.withAlpha(50)
: Colors.transparent,
),
width: ScreenUtil().setWidth(30),
height: ScreenUtil().setHeight(16),
child: Center(
child: Text(
letter,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: ScreenUtil().setSp(Dimens.superfine),
color: _onTap == letter
? Colours.white
: Colours.darkGray,
fontWeight: Weights.bold,
),
))))))
.toList(),
)));
///跳转到指定的字母位置
void jumpToLetter(String letter) {
final index = _presenter.findLastStartLetterIndex(letter);
//计算顶部到[AllContacts]的固定距离
// final RenderBox top = _topHightGlobalkey.currentContext.findRenderObject();
// final RenderBox contactsGroupTitle =
// _contactsGroupTitleGlobalKey.currentContext.findRenderObject();
// final RenderBox contacts =
// _contactsGlobalKey.currentContext.findRenderObject();
// final itemHight = contacts.size.height / (_presenter.currentContactsLength);
final itemHight = ScreenUtil().setHeight(90);
if (index != -1) {
_letterPositions[letter] = index * itemHight;
print('itemHight:$itemHight');
print('高度:${_letterPositions[letter]}');
}
if (_letterPositions.isNotEmpty) {
final _pos = _letterPositions[letter];
if (_pos != null) {
_scrollController.animateTo(
_pos.clamp(0, _scrollController.position.maxScrollExtent),
duration: const Duration(microseconds: 200),
curve: Curves.easeOut);
}
}
}
当前的index:7
flutter: position:Offset(409.0, 204.3) //detail获取的全局坐标
flutter: position:Offset(22.2, -142.1) //detail获取的局部坐标
flutter: position:Offset(409.0, 204.3) //获取indexBar的触摸点全局坐标转换为局部坐标
flutter: hight:27.25925925925926
可以看到通过方法体_buildLetterIndexView()
来生成的的indexBar
,我们是无法通过context获取到它内部的相对坐标的。也就是说这里通过final RenderBox rb = context.findRenderObject();
获取的是外部的Widget
class _IndexBarState extends State<IndexBar> {
///用于控制字母的对应位置
Map<String, double> _letterPositions = {};
///第一个字母开始的位置
///按下时的标志
String _onTap = '';
///跳转到指定的字母位置
void jumpToLetter(String letter) {
final index = widget.presenter.findLastStartLetterIndex(letter);
final itemHight = ScreenUtil().setHeight(90);
if (index != -1) {
_letterPositions[letter] = index * itemHight;
print('itemHight:$itemHight');
print('高度:${_letterPositions[letter]}');
}
if (_letterPositions.isNotEmpty) {
final _pos = _letterPositions[letter];
if (_pos != null) {
widget.scrollController.animateTo(
_pos.clamp(0, widget.scrollController.position.maxScrollExtent),
duration: const Duration(microseconds: 200),
curve: Curves.easeOut);
}
}
}
@override
Widget build(BuildContext context) {
return Positioned(
// top: getScreenUtilHeight(55),
right: 0,
bottom: 75,
width: ScreenUtil().setHeight(30),
child: Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.symmetric(vertical: ScreenUtil().setHeight(5)),
color: Colors.transparent,
// color: Colors.amberAccent,
child: Column(
children: widget.presenter
.getIndexTags()
.map((letter) => GestureDetector(
onTapDown: (details) {
setState(() {
_onTap = letter;
jumpToLetter(letter);
});
},
onTapUp: (details) {
setState(() {
_onTap = '';
});
},
onLongPressStart: (details) {
setState(() {
_onTap = letter;
});
jumpToLetter(letter);
},
onLongPressEnd: (details) {
setState(() {
_onTap = '';
});
},
onVerticalDragStart: (start) {
//获取滑动点击的屏幕坐标
//获取点触摸到的标签的父盒子,indexBar
final RenderBox rb = context.findRenderObject();
//计算触摸点在父盒子中的相对位置
final local = rb.globalToLocal(start.globalPosition);
//获取单个标签的高度
final double hight = rb.size.height /
widget.presenter.getIndexTags().length;
print('hight:$hight');
//求当前触摸的标签的index
final int index = (local.dy / hight).round();
print('当前的index:$index');
setState(() {
if (index <= 0) {
_onTap = widget.presenter.getIndexTags().first;
} else if (index >=
widget.presenter.getIndexTags().length) {
_onTap = widget.presenter.getIndexTags().last;
} else {
_onTap = widget.presenter.getIndexTags()[index];
}
});
jumpToLetter(_onTap);
},
onVerticalDragUpdate: (details) {
//获取滑动点击的屏幕坐标
//获取点触摸到的标签的父盒子,indexBar
final RenderBox rb = context.findRenderObject();
//计算触摸点在父盒子中的相对位置
print('position:${details.localPosition}');
print('position:${details.localPosition}');
var local = rb.globalToLocal(details.globalPosition);
print('position:${details.localPosition}');
//获取单个标签的高度
final double hight = rb.size.height /
widget.presenter.getIndexTags().length;
print('hight:$hight');
//求当前触摸的标签的index
final int index = (local.dy / hight).round();
print('当前的index:$index');
setState(() {
if (index <= 0) {
_onTap = widget.presenter.getIndexTags().first;
} else if (index >=
widget.presenter.getIndexTags().length) {
_onTap = widget.presenter.getIndexTags().last;
} else {
_onTap = widget.presenter.getIndexTags()[index];
}
});
jumpToLetter(_onTap);
},
onVerticalDragEnd: (details) {},
child: Padding(
padding: EdgeInsets.only(
top: ScreenUtil().setHeight(1),
bottom: ScreenUtil().setHeight(1)),
child: Container(
// color: Colors.blueAccent,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(80),
color: _onTap == letter
? Colours.black.withAlpha(50)
: Colors.transparent,
),
width: ScreenUtil().setWidth(30),
height: ScreenUtil().setHeight(16),
child: Center(
child: Text(
letter,
textAlign: TextAlign.center,
style: TextStyle(
fontSize:
ScreenUtil().setSp(Dimens.superfine),
color: _onTap == letter
? Colours.white
: Colours.darkGray,
fontWeight: Weights.bold,
),
))))))
.toList(),
)));
}
}
当前的index:19
flutter: itemHight:81.57635467980296
flutter: 高度:1631.5270935960593
flutter: position:Offset(401.3, 523.0) //detail获取的全局坐标
flutter: position:Offset(14.5, 307.0) //detail获取的局部坐标
flutter: position:Offset(14.5, 311.6) //获取indexBar的触摸点全局坐标转换为局部坐标
flutter: hight:16.650976099251963
将IndexBar
封装成一个Widegt,此时的全局坐标与局部坐标并不一致,说明此时通过final RenderBox rb = context.findRenderObject();获取的的确是'IndexBar'自身了
所以,导致滑动时,滑动标签项与手势滑动不一致的原因在于context没有真正拿到IndexBar
,获取到局部坐标。