This is the 15th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021
The index of article
There is too much functionality here so create a new file to write the index bar. The index bar is superimposed on top of the Listview, so the body in the moments page should use the Stack. Then add the index bar.
body: Stack( children: [ Container( child: ListView.builder( itemBuilder: _itemForRow, itemCount: Datas.length + _headerdata.length,), color: weChatThemColor,), // IndexBar(),Copy the code
Add index bar data
Const INDEX_WORDS = [' 🔍 ', 'being', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I' and 'J' and 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ];Copy the code
Create a list of widgets
final List<Widget> words = [];
Copy the code
Add widgets in initState
for (int i = 0; i < INDEX_WORDS.length; i++) { words.add( Expanded(child: Text(INDEX_WORDS[i],style: TextStyle(fontSize: 10,color: Colors.grey),),) ); }Copy the code
Then write the index bar interface in build so that the index bar can be displayed.
Jam (right: 0.0, top: screenHeight(context)/8, height: screenHeight(context)/2, width: 30, child: Column( children: words, ), );Copy the code
The index bar also needs to capture which one is selected, so you define two attributes first.
Color _bkColor = color.fromrgbo (1, 1, 1, 0.0); Color _textColor = Colors.black;Copy the code
Then Column is wrapped with the GestureDetector.
Change the text color of the text created previously in initState to _textColor. Change the color of Column to _bkColor after wrapping it in Container.
Listen on onVerticalDragDown and onVerticalDragEnd at GestureDetector, then setState changes the background color and text color.
OnVerticalDragDown: (DragDownDetails details){setState(() {_bkColor = const color.fromrgbo (1, 1, 1, 0.5); _textColor = Colors.white; }); }, onVerticalDragEnd: (DragEndDetails details){setState(() {_bkColor = const color.fromrgbo (1, 1, 1, 0.0); _textColor = Colors.black; }); },Copy the code
The background color changes, but the text color does not, because the text color is in initState. So interface related items should not be placed in data related areas, otherwise bugs will occur, so words and loops will be placed in build.
Then listen on the onVerticalDragUpdate at the GestureDetector, use context.findRenderObject to find the nearest widget, and use globalToLocal to calculate the y value from the current click to the widget’s origin. And then figure out the height of the character, figure out what item it is, and then you get the letter that you click on.
OnVerticalDragUpdate: (DragUpdateDetails details){RenderBox box = context.findRenderObject() as RenderBox; Double y = box. GlobalToLocal (details.globalPosition).dy; double y = box. Var itemHeight = screenHeight(context) / 2 / index_words.length; Int index = (y ~/ itemHeight).clamp(0, INDEX_WORDS.length-1); print('${INDEX_WORDS[index]}'); },Copy the code
Encapsulate the steps inside as a method
String getIndex (BuildContext Context,Offset globalPosition) {RenderBox box = context.findRenderObject() as RenderBox; Double y = box. GlobalToLocal (globalPosition).dy; double y = box. Var itemHeight = screenHeight(context) / 2 / index_words.length; Int index = (y ~/ itemHeight).clamp(0, INDEX_WORDS.length-1); return INDEX_WORDS[index]; }Copy the code
onVerticalDragUpdate
onVerticalDragUpdate: (DragUpdateDetails details) {
getIndex(context, details.globalPosition);
},
Copy the code
I need to return the data to the address book page, so I’ll write a callback here.
final void Function(String str)? indexBarCallBack;
IndexBar({this.indexBarCallBack});
Copy the code
Call indexBarCallBack inside onVerticalDragUpdate.
onVerticalDragUpdate: (DragUpdateDetails details) { if (widget.indexBarCallBack ! = null) { widget.indexBarCallBack! ( getIndex(context, details.globalPosition)); }},Copy the code
Pass in the indexBarCallBack using IndexBar outside
IndexBar(indexBarCallBack: (String str){
}),
Copy the code
The next step is to scroll according to the letter clicked, so you need to calculate the scrolling distance of each letter. I need to declare a ScrollController
ScrollController _scrollController = ScrollController();
Copy the code
Set it to controller of _scrollController.
controller: _scrollController,
Copy the code
Then declare map, _cellHeight, and _groupHeight
final Map _groupOffsetMap = {};
final double _cellHeight = 54.5;
double _groupHeight = 30;
Copy the code
Then calculate the scrolling distance in initState.
var _groupOffset = _cellHeight * _headerData.length; // Calculate the position of each header through the loop and put it into the dictionary. for (int i = 0; i < _listDatas.length; I ++) {if (I < 1){// The first cell must have a header _groupoffsetmap.addall ({_listDatas[I].indexLetter:_groupOffset}); _groupOffset += _cellHeight + _groupHeight; } else if (_listDatas[i].indexLetter == _listDatas[i-1].indexLetter ) { _groupOffset += _cellHeight; } else { _groupOffsetMap.addAll({_listDatas[i].indexLetter:_groupOffset}); _groupOffset += _cellHeight + _groupHeight; }}Copy the code
You can then scroll through the IndexBar callback.
IndexBar(indexBarCallBack: (String str){
if (_groupOffsetMap[str] != null) {
_scrollController.animateTo(
_groupOffsetMap[str], duration: Duration(microseconds: 100),
curve: Curves.easeIn);
}
}),
Copy the code
For the left side of the bubble, wrap the Indexbar of the Indexbar.
Then add images and text to the Container.
Container(alignment (0,-1.1), width: 100, color: Colors. Red, child: Stack(alignment: 0,-1.1) Alignment(-0.2, 0), children: [Image(Image: AssetImage('images/ bubble.png '), width: 60,), Text('A', style: TextStyle( fontSize: 35, color: Colors.white, ), ), ], ), ),Copy the code
You then need to transform the bubble text depending on where you click, and hide the bubble when you release it, so you declare three properties.
Double _indicatorY = 0.0; String _indicatorText = "A"; bool _indicatorHidden = true;Copy the code
Change getIndex to return index only, then change _indicatorY and _indicatorText in onVerticalDragDown and onVerticalDragUpdate, and set _indicatorHidden to false. And then inside onVerticalDragEnd set _indicatorHidden to true.
onVerticalDragDown: (DragDownDetails details) { int index = getIndex(context, details.globalPosition); if (widget.indexBarCallBack ! = null) { widget.indexBarCallBack! ( INDEX_WORDS[index]); } setState(() {_indicatorY = 2.2 / index_words.length * index - 1.1; _indicatorText = INDEX_WORDS[index]; _indicatorHidden = false; _bkColor = const color.fromrgbo (1, 1, 1, 0.5); _textColor = Colors.white; }); }, onVerticalDragUpdate: (DragUpdateDetails details) { int index = getIndex(context, details.globalPosition); if (widget.indexBarCallBack ! = null) { widget.indexBarCallBack! ( INDEX_WORDS[index]); } setState(() {_indicatorY = 2.2 / index_words.length * index - 1.1; _indicatorText = INDEX_WORDS[index]; _indicatorHidden = false; }); }, onVerticalDragEnd: (DragEndDetails details) {setState(() {_bkColor = const color.fromrgbo (1, 1, 1, 0.0); _textColor = Colors.black; _indicatorHidden = true; }); },Copy the code
Then modify the bubble side and return null if _indicatorHidden is true, otherwise return the bubble and the position of the bubble changes as _indicatorY changes.
Container( alignment: Alignment(0,_indicatorY), width: 100, color: Colors.red, child: _indicatorHidden ? Null: Stack(alignment: alignment (-0.2, 0), children: [Image(Image: AssetImage('images/ bubble.png ')), width: 60, ), Text( _indicatorText, style: TextStyle( fontSize: 35, color: Colors.white, ), ), ], ), ),Copy the code