3

In my app, I have a PageView and a Slider. When I scroll the PageView, I expect the Slider to change its value, and when I slide the Slider I expect the page of the PageView to change as well. For this, I have a state to be the value of the Slider, and in onChanged I will have the change its value, and in onChangeEnd will animate the PageView to the desire position:

Slider(
   value: _sliderValue,
   onChanged: (val) {
      setState(() {
         _sliderValue = val;
      });
   },
   onChangeEnd: (val) {
      _controller.animateToPage(val.toInt(),
          duration: Duration(milliseconds: 300), curve: Curves.ease);
      },
   max: (pages.length - 1).toDouble())

And in PageView, I have to update the _sliderValue as well, so it works when I scroll the PageView:

PageView.builder(
  onPageChanged: (page) {
    setState(() {
       _sliderValue = page.toDouble();
    });
  },
)

Problem is, I now have 2 places that I change the _sliderValue - one in onChanged for the Slider to be responsive, one in onPageChanged to work with scrolling. This makes the slider jarring when I press repeatedly on it.

Is there any way to solve this issue?

Kise
  • 2,823
  • 1
  • 24
  • 43

2 Answers2

4

Actually, the problem in this case lies in the fact that for every page change, the onPageChanged callback of the PageView is triggered. For example if we are at page 1 and use the Slider to move to page 5, then onPageChanged will be called 4 times (for page 2-3-4-5) and as a result the value state of the Slider will be changed repeatedly for 4 times. This causes the thump of the Slider to jump all over the places.

I attempted to fix it by having a Timer inside onPageChanged to wait for a certain amount of time before trigger the setState, so it only fires after the last page change is completed.

// Define a Timer object in your state
Timer _timer;
PageView.builder(
  onPageChanged: (page) {
    // If there is a Timer running, cancel it
    if (_timer != null) {
      _timer.cancel();
    }
    // Setup a new Timer
    _timer = Timer(Duration(milliseconds: 300), () {
      setState(() {
        _sliderValue = page.toDouble();
      });
    });
  },
)

This might seem hacky and the result is actually not perfect (when you scroll the page manually, the Slider will have to wait 300ms before updating its value) but it is an improvement nonetheless. If anyone has a better solution, I'm happy to hear it.

Kise
  • 2,823
  • 1
  • 24
  • 43
0

Please use val.round() instead of val.toInt().

            Slider(
            value: _sliderValue,
            onChanged: (val) {
              setState(() {
                _sliderValue = val.round().toDouble(); // this line changed.
              });
            },
            onChangeEnd: (val) {
              _controller.animateToPage(val.round(), // this line changed.
                  duration: Duration(milliseconds: 300), curve: Curves.ease);
            },
            max: (pages.length - 1).toDouble()),