This is the 15th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021.
- Our last article achieved the index bar display and click state change, next we want to achieve the index bar bubble display and address book linkage.
1. Figure out what text to click or drag
The idea is to return the corresponding letter by calculating the position of each letter in the index bar. We get the current widget’s box from the context, the RenderBox is the size of the index bar, and we use globalToLocal to convert the external coordinates to the current control’s coordinate system. Just like when we convert the child view point from the global coordinate point to the parent view coordinate, we don’t have to convert.
onVerticalDragUpdate: (DragUpdateDetails details){
setState(() {
RenderBox box = context.findRenderObject() as RenderBox ;
print('$box.globalToLocal(details.localPosition).dy');
print('Gesture moving position:$details');
});
},
Copy the code
Get the y value, the distance (x,y) from globalToLocal’s current position to the origin of the widget (upper left corner of the widget). Pass the height of each character according to the offset
/ / drag
onVerticalDragUpdate: (DragUpdateDetails details){
setState(() {
RenderBox box = context.findRenderObject() as RenderBox ;
double y = box.globalToLocal(details.localPosition).dy;
/ / the item level
var itemHeight = screenHeight(context)/2 / INDEX_WORDS.length;
int index = y ~/ itemHeight;
print('Selects number one$indexA ');
});
},
Copy the code
This will also get an error if it goes beyond the screen array
We also need to set the index range using clamp from 0 to index_words.length-1 to get the corresponding subscript and finally extract STR from the array. Let’s pull out the method
// Get the character of the selected Item!!
String getIndex(BuildContext context, Offset globalPosition) {
// Get the widget box before clicking
RenderBox box = context.findRenderObject() as RenderBox ;
// Get the y value, the distance (x,y) from the origin of the widget (top left corner of the widget)
double y = box.globalToLocal(globalPosition).dy;
// Figure out the height of the character
var itemHeight = screenHeight(context) / 2 / INDEX_WORDS.length;
// Count the number of items
int index = (y ~/ itemHeight).clamp(0, INDEX_WORDS.length - 1);
return INDEX_WORDS[index];
}
Copy the code
2. Index bar callback
We’re calling back a click on the return character in the index bar, and we need the high speed outside of the ListView, so we want a callback, and we define a callback method.
class CloumnIndexBar extends StatefulWidget {
final void Function(String str) indexBarCallBack;
CloumnIndexBar({required this.indexBarCallBack});
@override
_CloumnIndexBarState createState() => _CloumnIndexBarState();
}
Copy the code
We pass this character around as we click and move
/ / drag
onVerticalDragUpdate: (DragUpdateDetails details){
setState(() {
var str = getIndex(context, details.globalPosition);
widget.indexBarCallBack(str);
print(str);
});
},
/ / click
onVerticalDragDown: (DragDownDetails details){
setState(() {
var str = getIndex(context, details.globalPosition);
widget.indexBarCallBack(str);
print('Gesture into position:$details');
_backgroundColor = Color.fromRGBO(1.1.1.0.5);
_textColor = Colors.white;
});
},
Copy the code
We get a callback at creation time
ListView scrolling requires a controller, so we need to create it at the beginning, which is defined directly here
final ScrollController _scrollController = ScrollController();
Copy the code
We use animateTo of _scrollController for scrolling, so let’s do a 300 test here
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('is selected$str'); },) ",Copy the code
So we can figure out where our letters correspond.
3. Calculate the offset
The height of the cell and the height of the head we defined earlier
double _cellHeight = 54.5;
double _groupHeight = 30.0;
Copy the code
Then we define a dictionary in which each letter contains the corresponding offset
final Map _groupOffsetMap = {
INDEX_WORDS[0] :0.0,
INDEX_WORDS[1] :0.0};Copy the code
Here the first two are fixed, so there’s no offset. We can’t put it in itemBulid when we do the calculation, it only loads when we slide it, so we do the calculation in initState after the data is processed.
var _groupOffset = _cellHeight * _headerData.length;// Default location
// Enter the loop to calculate the position of each head. In the dictionary
for (int i = 0; i < _listDatas.length; i++) {
if (i < 1) {
// The first cell must have a head!
_groupOffsetMap.addAll({_listDatas[i].indexLetter: _groupOffset});
// add _groupOffset
_groupOffset += _cellHeight + _groupHeight;
} else if (_listDatas[i].indexLetter == _listDatas[i - 1].indexLetter) {
// Add the height of the Cell
_groupOffset += _cellHeight;
} else {
_groupOffsetMap.addAll({_listDatas[i].indexLetter: _groupOffset});
// add _groupOffset_groupOffset += _cellHeight + _groupHeight; }}Copy the code
We showed it in the previous callback, and we added a judgment in case it didn’t exist
if(_groupOffsetMap[str] ! =null) {
_scrollController.animateTo(_groupOffsetMap[str],
duration: Duration(microseconds: 100),
curve: Curves.easeIn);
}
print('is selected$str');
Copy the code
4. Indicator display
The bubble to the left of the indicator can be treated as an integral part of the indicator, so we can use row for layout.
Let’s adjust the width of the index bar and the overall width so thatIn the middle
the
Let’s go ahead and set the one on the leftindicator
Mostly bubbles and text so we can usestack
On the parcel
Let’s reposition it, center it
We know that the coordinates in flutter are1 to 1
0 in the middle, so we can usealignment
To represent theThe scope of
I’m going to go a little bit further down here and it’s going to be in the range of y(-1.1, 1.1)
We define three variables,Figure out where -1.1 is at 1.1 based on index
double _indexBarOffsetY = 0.0; // The center of the offset between -1.1 and 1.1 is 0
bool _indexBarHidden = true; // Whether to hide
String _indexBarText = 'A'; // The letter currently being displayed
Copy the code
We’re going to hide it when we click or drag it and we’re going to change this string that was returned to index
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/bubbles. PNG'),
width: 60,
),
Text(_indicatorText,
style: TextStyle(fontSize: 35, color: Colors.white),
)
],
),
),
GestureDetector(
/ / drag
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;
});
});
},
/ / click
onVerticalDragDown: (DragDownDetails details){
setState(() {
int index = getIndex(context, details.globalPosition);
widget.indexBarCallBack(INDEX_WORDS[index]);
print('Gesture into position:$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;
});
},
/ / leave
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,
),
),
)
],
),
)
);
}
Copy the code
The end result: