Twisha Solgama
Twisha Solgama

Reputation: 150

How to disable scroll in PageView in a particular direction in flutter

I'm new to flutter and am learning about Pageview. In my app I have made an onboarding screen. Is there a way to disable scroll in a particular direction? What I'm trying to achieve is that the user should not be able to scroll/swipe left when they are on the first page and should not be able to scroll/swipe right when they are on the last page. I know you can disable scroll physics by using NeverScrollableScrollPhysics(), but is there a way to give it a direction?

Code:

PageView(
  onPageChanged: (newValue) {
    //Update the page
  },
  physics: activePage == 0 ||
          activePage == pages.length - 1
      ? const NeverScrollableScrollPhysics()
      : const PageScrollPhysics(),
  controller: pageController,
  children: [
    for (var page in pages) PageWidget(page: page),
  ],
)

Upvotes: 0

Views: 2763

Answers (5)

Τhalis Apostolatos
Τhalis Apostolatos

Reputation: 1

I had been stuck on trying to implement this behavior for a while now and today I managed to overcome my problem.

What I actually needed is to completely disable forward scrolling only when the user swipes forward in my application.

I still wanted to allow the user to proceed forward after completing some necessary steps

To implement disabling change of page on user-swipe follow the steps:

  1. Create a PageController instance final PageController _pageController = PageController();

  2. Keep a reference of the current page - very important int _currentPage = 0

  3. When creating PageView use AlwaysScrollableScrollPhysics():

          child: PageView(
            controller: _pageController,
            physics: AlwaysScrollableScrollPhysics(),
            onPageChanged: (index) {
              // Revert the conditions if you wish to disable backwards scrolling
              if (index > _currentPage) {
                print("DISABLED FORWARD");
                /*
                onPageChanged runs first only in case of swipe gestures
                Because of this, if users swipe forward then they should not
                be allowed to proceed and pageController takes them back to
                the page the were on
                 */
                _pageController.animateToPage(_currentPage, duration: Duration(milliseconds: 200), curve: Curves.easeIn);
              } else if (index < _currentPage) {
                /*
                This runs only when the user SWIPES backwards
                 */
                print("ALLOWED BACKWARD");
              } else {
                /*
                This is called in the case of the user triggering a function that should allow going forward/backward. The reason we need this else case is the following: If the user completes a step of the process or opts to go back NOT BY SWIPING but by using for example some back button the setState method in the custom functions goToNextPage/goToPreviousPage (see step 4) gets called before onPageChanged. Thus the _currentPage is equal to the index of the new page.
                 */
                print("USER TRIGGERED ACTION");
              }
            },

            children: [
              // your page view children
            ],
          ),

  1. If, like me, you have some actions that should trigger forward/backward scrolling no matter what (eg proceeding in the next step of a form/going back) you should implement two more functions to handle user triggered actions
void goToNextPage() {
  // maxPageInt is the total number of your pages
  if (_currentPage < maxPageInt) {
    setState(() {
      _currentPage++;
      // Do anything you want here 
    });
    _pageController.animateToPage(
        _currentPage,
        duration: Duration(milliseconds: 300),
        curve: Curves.easeInOut,
      );
}

void goToPreviousPage() {
  if (_currentPage > 0) {
    setState(() {
      _currentPage--;
      // Do anything you want here 
    });
    _pageController.animateToPage(
        _currentPage,
        duration: Duration(milliseconds: 300),
        curve: Curves.easeInOut,
      );

These two functions should only be called when the user for example completes a step. Suppose you have a function onCompletedStep you should do:

void onCompletedStep() {
  // Do anything you wish here
  goToNextPage();
}

Or if you have a back button that calles onBackPressed you should do:

void onBackPressed() {
  // Do anything here
  goToPreviousPage();
}

Upvotes: 0

Twisha Solgama
Twisha Solgama

Reputation: 150

Found the answer. Simply set physics of the PageView to ClampingScrollPhysics().

Upvotes: 0

Arshad
Arshad

Reputation: 45

To disable scrolling in a particular direction in a PageView widget in Flutter, you can use a combination of physics and onPageChanged callback to control the scroll behavior. For example. import 'package:flutter/material.dart';

class DisableScrollDirectionPageView extends StatefulWidget {
  @override
  _DisableScrollDirectionPageViewState createState() =>
      _DisableScrollDirectionPageViewState();
}

class _DisableScrollDirectionPageViewState
    extends State<DisableScrollDirectionPageView> {
  PageController _pageController;
  int _currentPage = 0;

  @override
  void initState() {
    super.initState();
    _pageController = PageController(initialPage: _currentPage);
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  void _handlePageChange(int page) {
    setState(() {
      _currentPage = page;
    });
  }

  bool _isScrollable(int page) {
    // Enable scrolling only for pages other than page 2
    return page != 2;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Disable Scroll Direction'),
      ),
      body: PageView(
        controller: _pageController,
        physics: _isScrollable(_currentPage)
            ? AlwaysScrollableScrollPhysics()
            : NeverScrollableScrollPhysics(),
        onPageChanged: _handlePageChange,
        children: <Widget>[
          Container(
            color: Colors.blue,
            child: Center(
              child: Text(
                'Page 1',
                style: TextStyle(fontSize: 24, color: Colors.white),
              ),
            ),
          ),
          Container(
            color: Colors.red,
            child: Center(
              child: Text(
                'Page 2',
                style: TextStyle(fontSize: 24, color: Colors.white),
              ),
            ),
          ),
          Container(
            color: Colors.green,
            child: Center(
              child: Text(
                'Page 3',
                style: TextStyle(fontSize: 24, color: Colors.white),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

In the above code DisableScrollDirectionPageView widget creates a PageView with three pages. The _isScrollable method checks the current page index and returns true if scrolling is enabled for that page, and false otherwise. The physics property of the PageView is set to either AlwaysScrollableScrollPhysics() or NeverScrollableScrollPhysics() based on the _isScrollable value.

Upvotes: 0

Priyanshu Paliwal
Priyanshu Paliwal

Reputation: 2398

To get that kind of behaviour you can give controller to pageview and get swipe direction.

Then you can block the scroll according to you. Refer the link below

https://stackoverflow.com/a/63188690/15438326

Upvotes: 0

Priyanshu Paliwal
Priyanshu Paliwal

Reputation: 2398

You can achieve this behaviour like this

set physics as NeverScrollableScrollPhysics() then

you can have a next button, where you can use pageView.animateToPage() function to animate to next page

This will give you the feel of swiping and can achieve this behaviour.

Here by using this you can only move forward not backward in your pageView

If you have any doubts let me know

Upvotes: 0

Related Questions