Проведя более глубокий анализ моих собственных потребностей и проверив исходный код виджета PageView
, я понял, что мне нужен виджет с прокруткой, который работает по элементам, но в то же время мне нужно, чтобы пространство для каждого элемент был таким же, как и обычный скроллер, поэтому мне нужно было изменить ScrollPhysics
обычного скроллера. В найденном этом сообщении, описывающем физику прокрутки при флаттере, можно найти до некоторой степени и было близко к моим потребностям, разница заключалась в том, что мне нужно было добавить пространство по бокам текущего видимого виджета, а не только справа.
Итак, я взял CustomScrollPhysics
в сообщении и изменил его таким образом (измененные части почтового индекса закрашены комментариями <--
и -->
:
class CustomScrollPhysics extends ScrollPhysics {
final double itemDimension;
const CustomScrollPhysics(
{required this.itemDimension, ScrollPhysics? parent})
: super(parent: parent);
@override
CustomScrollPhysics applyTo(ScrollPhysics? ancestor) {
return CustomScrollPhysics(
itemDimension: itemDimension, parent: buildParent(ancestor));
}
double _getPage(ScrollMetrics position, double portion) {
// <--
return (position.pixels + portion) / itemDimension;
// -->
}
double _getPixels(double page, double portion) {
// <--
return (page * itemDimension) - portion;
// -->
}
double _getTargetPixels(
ScrollMetrics position,
Tolerance tolerance,
double velocity,
double portion,
) {
// <--
double page = _getPage(position, portion);
// -->
if (velocity < -tolerance.velocity) {
page -= 0.5;
} else if (velocity > tolerance.velocity) {
page += 0.5;
}
// <--
return _getPixels(page.roundToDouble(), portion);
// -->
}
@override
Simulation? createBallisticSimulation(
ScrollMetrics position, double velocity) {
// If we're out of range and not headed back in range, defer to the parent
// ballistics, which should put us back in range at a page boundary.
if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) ||
(velocity >= 0.0 && position.pixels >= position.maxScrollExtent)) {
return super.createBallisticSimulation(position, velocity);
}
final Tolerance tolerance = this.tolerance;
// <--
final portion = (position.extentInside - itemDimension) / 2;
final double target =
_getTargetPixels(position, tolerance, velocity, portion);
// -->
if (target != position.pixels) {
return ScrollSpringSimulation(spring, position.pixels, target, velocity,
tolerance: tolerance);
}
return null;
}
@override
bool get allowImplicitScrolling => false;
}
Таким образом, я взял половину дополнительного пространства, оставленного текущим видимым виджетом (например, (position.extentInside - itemDimension) / 2
), и добавил его в расчет страницы на основе положения прокрутки, что позволило виджету быть меньше видимого размера прокрутки, но с учетом весь экстент как одну страницу, и вычесть его из расчета пикселей прокрутки на основе страницы, предотвращая размещение страницы за половиной видимой части виджетов по бокам или перед ними.
Другое изменение заключается в том, что itemDimension
- это не размер прокрутки, деленный на количество элемента, мне нужно, чтобы это значение было размером каждого виджета в направлении прокрутки.
Вот что у меня получается:
Конечно, у этой реализации есть некоторые ограничения:
- Размер каждого элемента в направлении прокрутки должен быть фиксированным, если один элемент имеет другой размер, тогда вся прокрутка ведет себя хаотично.
- Размер должен включать отступы, если они есть, иначе это будет иметь тот же эффект, что и виджеты разных размеров.
Я не сосредотачивался на решении этих ограничений и создании более полного виджета, потому что эти ограничения гарантированы в случае, если мне нужен этот виджет. Вот полный код приведенного выше примера.
https://gist.github.com/rolurq/5db4c0cb7db66facf8f5a59396faeec7
person
Rolando Urquiza
schedule
16.08.2019