Чтобы иметь что-то вроде этого:
Начальная часть
Чтобы решить эту проблему, вам нужно создать собственный виджет, которыйвернет LayoutBuilder
, который содержит Stack
, который содержит ListView
на первом слое и Container
на втором слое (как скроллер) справа (чтобы поместить ваш скроллер справа, добавьте alignment: Alignment.topRight
к вашему Stack
).
Перемещение скроллера
Чтобы переместить скроллер, добавьте GestureDetector
и поймайте onVerticalDragUpdate
, добавьте пользовательское свойство _offsetContainer
к тому, которое вы добавите details.delta.dy
(из события), чтобы прокрутить ваш скроллер вниз и вверх.
Переместить ListView из скроллера
Первая часть
После этого вам нужно добавитьScrollController
к вашему ListView
для управления его вертикальным перемещением (как свойство controller
).
Хорошо, теперь вам нужно знать кратные данные, чтобы продолжить:
- Ваш алфавитный массивдлина
- размер высоты вашего
ListView
- размер высоты скроллера
- есть элемент colлекция отсортирована (логика)
- Размер высоты одного элемента вашего
ListView
(который мы назовем _itemsizeheight
)
Цель, которую нужно достичь сейчас, состоит в том, чтобы заставить вашListView
на первый элемент в списке, начиная с выбранной буквы алфавита.
Для этого вам понадобятся некоторые математические и некоторые трюки.
Чтобы рассчитать ваш ListView
рост, вам нужно взятьвысота тела и убрать высоту другого виджета.Но для упрощения здесь мы скажем, что ваш ListView
принимает весь рост.Тогда это соответствует contrainsts.biggest.height
(благодаря LayoutBuilder
).
Чтобы рассчитать высоту скроллера, вы можете сделать это эффективно и разделить высоту тела на количество букв в вашем алфавитном массиве._heightscroller = contrainsts.biggest.height / _alphabet.length;
Вторая часть
Сначала мы заставим скроллер показывать индекс букв с помощью виджета Text
.Затем добавьте виджет Text
в свой контроллер и добавьте свойство _text
в свой пользовательский виджет, чтобы вы могли изменить его с помощью функции setState
.
В вашем onVerticalDragUpdate
у вас есть _offset
значение, соответствующее вашей позиции скроллера.Затем вам нужно сделать «простой» калькулятор, чтобы найти соответствующий ему индекс вашего массива алфавитов.
_text = _alphabet[((_offsetContainer / _heightscroller) % _alphabet.length).round()];
Третья часть
Теперь вы знаете букву, тогда будет легкосделай ход своим ListView
.Сначала получите в своем списке индекс первого элемента, который соответствует выбранной букве.
Пример:
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;
}
}
Теперь, когда у вас есть индекс, просто используйте ваш ScrollController
для перемещенияListView
.
_scrollController.animateTo(index * _itemsizeheight,
duration: new Duration(milliseconds: 500),
curve: Curves.ease);
Сделать перемещение скроллера из ListView
Хорошо, теперь это работает, но нам нужно сделать перемещение скроллера тоже, не так ли?Я имею в виду, что я не хочу, чтобы он оставался на букве V
, когда ListView
отобразит мне A
буквенных знаков.
Как это сделать?
В вашей функции инициализации простопосле этого вы устанавливаете ScrollController
и добавляете _scrollController.addListener(onscrolllistview);
, чтобы знать, когда пользователь будет выполнять прокрутку непосредственно в ListView
.
. В вашей функции onscrolllistview
вам нужны некоторые вычисления, чтобы отобразить первый элемент ипоследний отображаемый элемент.
var indexFirst = ((_scrollController.offset / _itemsizeheight) % widget.items.length).floor();
var indexLast = ((((_heightscroller * _alphabet.length) / _itemsizeheight).floor() + indexFirst) - 1) % widget.items.length;
Отлично, теперь вы можете легко узнать букву ведьмы, которую нужно отобразить на скроллере, а затем узнать, где она должна быть (изменить ее смещение).
var i = _alphabet.indexOf(fletter);
if (i != -1) {
setState(() {
_text = _alphabet[i];
_offsetContainer = i * _heightscroller;
});
Значение fletter
будет зависеть от направления прокрутки.
Если вы идете вниз, это будет:
var fletter = widget.items[indexLast].toString().toUpperCase()[0];
Если вы собираетесьвверх это будет:
var fletter = widget.items[indexFirst].toString().toUpperCase()[0];
Исправьте анимацию скроллера
Хорошо, теперь, если вы поставите debugPrint
, вы увидите, что функция onscrolllistview
звонил все время, даже когда мы используем скроллер.Это проблема.Тогда как это исправить?
Вам нужно знать, когда вы закончили двигаться, когда все ваши анимации закончены.
Затем просто добавьте свойство _animationcounter
в ваш класс.Вы будете увеличивать его, когда перемещаете скроллер, и уменьшать его, когда одна анимация закончена.Как это:
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;
}
}
Теперь просто окружите свой код скроллера движущимся в onscrolllistview
на
if (_animationcounter == 0) {/*...your code...*/}
PS
Вам нужно позаботиться о себе _offsetContainer
, чтобы не выйти за пределы.Вы можете сделать это следующим образом:
if ((_offsetContainer + details.delta.dy) >= 0 &&
(_offsetContainer + details.delta.dy) <=
(context.size.height - _heightscroller)) {
_offsetContainer += details.delta.dy;
}
Это решение, вероятно, может быть оптимизировано (не отправляйте несколько анимаций, добавьте var, чтобы сделать меньше вызовов).Вы можете найти полную реализацию здесь: AtoZscrollflutter