PageView свайп в одну сторону
Егор Долгов · 15.08.2019По умолчанию виджет PageView прокручивается с помощью касания в обе стороны.
Чтобы запретить прокрутку с помощью касания, нужно в параметре physics задать значение NeverScrollableScrollPhysics() .
PageView(
physics: NeverScrollableScrollPhysics(),
children: [
MyPageFirst(),
MyPageSecond(),
...]
)//PageView
И тогда чтобы переходить от страницы к странице нужен объект PageController , заданный в параметре controller .
_pageController = PageController(initialPage: 0);
PageView(
physics: NeverScrollableScrollPhysics(),
controller: _pageController,
children: [
MyPageFirst(),
MyPageSecond(),
...]
)//PageView
Для перехода к другой странице нужно вызвать метод pageController.jumpToPage()
_pageController.jumpToPage(1);
Для анимированного перехода нужно использовать pageController.animateToPage()
_pageController.animateToPage(1, duration: Duration(milliseconds: 600), curve: Curves.linear);
Односторонняя прокрутка
Но что делать если мы хотим, чтобы пользователь мог прокрутить касанием только в одну сторону - например назад, для возвращения к предыдущему экрану. А к следующему экрану только с помощью бизнес логики приложения.
С помощью метода addListener зарегистрируем обработчик событий для объекта класса PageController .
_pageController.addListener((){
print('Текущее состояние прокрутки ${_pageController.page}');//тип double
print('Состояние прокрутки ${_pageController.position.userScrollDirection.index}');
// 0 - прокрутка происходит с помощью функций jumpToPage или animateToPage
// 1 - пользователь прокручивает "назад", к предыдущей странице
// 2 - пользователь прокручивает "вперед", к следующей странице
});
Такой обработчик события будет выводить в консоль сообщения, с текущим состоянием, во время прокрутки PageView.
Т.к. NeverScrollableScrollPhysics() блокирует все пользовательские жесты, мы не будем его использовать, а блокировать свайп вперед будем в обработчике событий.
Код обработчика односторонней прокрутки
_pageController.addListener((){
if (_pageController.position.userScrollDirection.index == 2 && indexPage < _pageController.page) _pageController.jumpToPage(indexPage);
else if (_pageController.position.userScrollDirection.index == 1 && _pageController.page == _pageController.page.roundToDouble()) {
indexPage = _pageController.page.toInt();
}
});
Что происходит в обработчике для односторонней прокрутки
indexPage - в этой переменной должен быть номер страницы, которую должен видеть пользователь.
- Если пользователь делает свайп для прокрутки вперед
_pageController.position.userScrollDirection.index == 2
- И если текущая позиция меньше той к которой может привести свайп вперед
&& indexPage < _pageController.page // это условие нужно чтобы небыло резкого перехода, когда мы делаем не доконца свайп назад, и возможно захотим "отменить действие свайпа"
- Делаем переход без анимации к установленной странице, чтобы прервать и отменить свайп вперед
_pageController.jumpToPage(indexPage);
- Если пользователь выполняет свайп назад
_pageController.position.userScrollDirection.index == 1
- И если текущее состояние положения прокрутки равно целому значения
&& _pageController.page == _pageController.page.roundToDouble() // Значит переход завершен
- Изменяем номер страницы которая должна быть открыта
indexPage = _pageController.page.toInt();
Функция бизнес логики для смены страницы
setPage(int page) {
if (indexPage == page) return;
indexPage = page;
_pageController.animateToPage(indexPage, duration: Duration(milliseconds: 600), curve: Curves.linear);
}
Метод onPageChanged в PageView
В виджете PageView можно задать метод onPageChanged
PageView(
onPageChanged:(int page){indexPage = page;},
physics: NeverScrollableScrollPhysics(),
controller: _pageController,
children: <Widget>[
MyPageFirst(),
MyPageSecond(),
...]
)//PageView
В данном примере не рекомендуем его использовать, и выполнять все проверки и смену состояния в одном месте: pageController.addListener((){}) .