How to get the finger movement event in flutter like 'recyclerview alphabet index in android' check the sample image.
I have created a Positioned alphabet index listview but I can't find the current index in DragUpdate.
var alphabet = ["#","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"];
new Positioned(
top: .0,
left: 1,
bottom: 10,
width: 55,
child: Material(
borderRadius: BorderRadius.circular(15.0),
elevation: 10.0,
child: ListView.builder(
itemCount: alphabet.length,
itemBuilder: (BuildContext context, int index) {
return new GestureDetector(
onVerticalDragUpdate:
(DragUpdateDetails detail) {
setState(() {
_barOffset += detail.delta.dy;
});
print("$detail");
print("Update ${alphabet[index]}");
},
onVerticalDragStart: (DragStartDetails detail) {
print("onVerticalDragStart");
print("Start ${alphabet[index]}");
},
onVerticalDragEnd: (DragEndDetails detail) {
print("onVerticalDragEnd");
print("End ${alphabet[index]}");
},
onTap: () => print(alphabet[index]),
child: new Container(
margin: EdgeInsets.only(
left: 20.0, right: 10.0, top: 6.5),
height: 15.0,
child: new Text('${alphabet[index]}'),
));
}),
),

Do the following steps to achieve this.
Step1: Gesture detector for A to Z slider
onVerticalDragUpdate: _onVerticalDragUpdate
onVerticalDragStart: _onVerticalDragStart
Step 2: Add the speech bubble for scrolled alphabet.
Step 3: calculate the index with vertically scrolled position
if ((_offsetContainer + details.delta.dy) >= 0 &&
(_offsetContainer + details.delta.dy) <=
(_sizeheightcontainer - _heightscroller)) {
_offsetContainer += details.delta.dy;
posSelected =
((_offsetContainer / _heightscroller) % _alphabet.length).round();
_text = _alphabet[posSelected];
if (_text != _oldtext) {
for (var i = 0; i < exampleList.length; i++) {
if (_text
.toString()
.compareTo(exampleList[i].toString().toUpperCase()[0]) == 0) {
_controller.jumpTo(i * _itemsizeheight);
break;
}
}
_oldtext = _text;
}
}
For the complete implementation, check this:
https://github.com/sanjaysingh1990/AtoZSliderCustomized.git
To have something like this:

To solve this problem you need to create a custom widget that will return a LayoutBuilder who contain a Stack who contain a ListView at first layer and a Container at second layer (as scroller) on right (to put your scroller on right add alignment: Alignment.topRight to your Stack).
To move the scroller add a GestureDetector and catch the onVerticalDragUpdate add use a custom property _offsetContainer to the one you will add the details.delta.dy (from the event) to make your scroller going down and up.
After that you need to add a ScrollController to your ListView to control it's vertical movement (as controller property).
Ok now you need to know multiples stuff to continue:
ListView
ListView (that we will call _itemsizeheight)The goal to reach now is to make move your ListViewat the first item in the list starting with the alphabet letter selected.
For that you need some maths and some tricks.
To calculate your ListView height you need to take the body height and remove the heigth of other widget. But to simplify here we will say that your ListView take all the body height. Then it correspond to contrainsts.biggest.height(Thanks to the LayoutBuilder).
To calculate your scroller height you can do it efficiently and divide the body height by the number of letter in your alphabet array.
_heightscroller = contrainsts.biggest.height / _alphabet.length;
We will first make the scroller show the letter index with a Text widget. Then add a Text widget to your controller and add a _text property in your custom widget to allow you to change it with the setState function.
In your onVerticalDragUpdate you have your _offset value that correspond to your scroller position. Then you need to make a 'simple' calc to find to witch index of your alphabet array it correspond.
_text = _alphabet[((_offsetContainer / _heightscroller) % _alphabet.length).round()];
Now you know the letter then it will be easy to make move your ListView.
First get the index in your list of the first element that correspond to the letter selected.
Example:
var index = 0;
for (var i = 0; i < widget.items.length; i++) {
if (widget.items[i].toString().trim().length > 0 &&
widget.items[i].toString().trim().toUpperCase()[0] ==
_text.toString().toUpperCase()[0]) {
index = i;
break;
}
}
Now that you have your index just use your ScrollController to move your ListView.
_scrollController.animateTo(index * _itemsizeheight,
duration: new Duration(milliseconds: 500),
curve: Curves.ease);
Ok now it's working but we need to make move the scroller too isen't ? I mean i dont want that it stay on the V letter when the ListView will display me A letter items.
How to do this ?
In your init function just after that you set your ScrollController add an
_scrollController.addListener(onscrolllistview); to know when the user will scroll directly in the ListView.
In your onscrolllistviewfunction you need some calculation to get the first item displayed and the last item displayed.
var indexFirst = ((_scrollController.offset / _itemsizeheight) % widget.items.length).floor();
var indexLast = ((((_heightscroller * _alphabet.length) / _itemsizeheight).floor() + indexFirst) - 1) % widget.items.length;
Perfect now you can easly know witch letter you need to display on your scroller and then know where it need to be (change it's offset).
var i = _alphabet.indexOf(fletter);
if (i != -1) {
setState(() {
_text = _alphabet[i];
_offsetContainer = i * _heightscroller;
});
The fletter var will depend of the direction of the scroll.
If you are going downward it will be :
var fletter = widget.items[indexLast].toString().toUpperCase()[0];
If you are going upward it will be :
var fletter = widget.items[indexFirst].toString().toUpperCase()[0];
Ok now if you put a debugPrint you will can see that the onscrolllistview function is called all time even when we are using the scroller. This is a problem. Then how to fix that ?
You need to know when you finished to move when all your animations are finished.
Then just add a _animationcounter property in your class.
You will increment it when you are moving your scroller and decrement it when one animation is finished.
Like this:
for (var i = 0; i < widget.items.length; i++) {
if (widget.items[i].toString().trim().length > 0 &&
widget.items[i].toString().trim().toUpperCase()[0] ==
_text.toString().toUpperCase()[0]) {
_animationcounter++;
_scrollController.animateTo(i * _itemsizeheight,
duration: new Duration(milliseconds: 500),
curve: Curves.ease) .then((x) => {_animationcounter--});
break;
}
}
Now just surround your code of scroller moving in onscrolllistview by
if (_animationcounter == 0) {/*...your code...*/}
You need to take care of you _offsetContainer to not go out of bound.
You cand do it like this by exemple:
if ((_offsetContainer + details.delta.dy) >= 0 &&
(_offsetContainer + details.delta.dy) <=
(context.size.height - _heightscroller)) {
_offsetContainer += details.delta.dy;
}
This solution can be probably optimized (not send multiple animation, add var make less calcs). You can find a complete implementation here : AtoZscrollflutter
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With