callstack / react-native-pager-view

React Native wrapper for the Android ViewPager and iOS UIPageViewController.
MIT License
2.74k stars 419 forks source link

[Android] onPageSelected callback fire before the ViewPager finishes navigating to the selected page with swiping. #650

Open batuhansahan opened 1 year ago

batuhansahan commented 1 year ago

Environment

React native v0.70.5 Pager View v6.1.0

Description

onPageSelected callback fire before the ViewPager finishes navigating to the selected page on Android. IOS is fine.

Changing screen with swipe that trigger screen change with the velocity help. In the middle of screen transition onPageSelected fires.

Reproducible Demo

The one with example folder

lc3t35 commented 1 year ago

This is very annoying : any solution ? I'm not enough kotlin friendly to fix this ;)

In our case, when the user touch the screen (not scrolling) on page 2 to open a new screen, when navigation goes back it goes to the page 1, not the previous page 2 as expected.

In reactnativepagerview/PagerViewViewManager.kt, I see eventDispatcher.dispatchEvent(PageSelectedEvent(host.id, vp.currentItem)) in createViewInstance

eventDispatcher.dispatchEvent(PageSelectedEvent(root.id, pageIndex)) in receiveCommand :

COMMAND_SET_PAGE, COMMAND_SET_PAGE_WITHOUT_ANIMATION -> {
        val pageIndex = args!!.getInt(0)
        val canScroll = childCount != null && childCount > 0 && pageIndex >= 0 && pageIndex < childCount
        if (canScroll) {
          val scrollWithAnimation = commandId == COMMAND_SET_PAGE
          setCurrentItem(view, pageIndex, scrollWithAnimation)
          eventDispatcher.dispatchEvent(PageSelectedEvent(root.id, pageIndex))
        }
      }

and here

override fun onPageSelected(position: Int) {
          super.onPageSelected(position)
          eventDispatcher.dispatchEvent(
                 PageSelectedEvent(host.id, position))
        }

I've comment these 3 eventDispatcher, and the same issue arise, so maybe it is canScroll / setCurrentItem that changes the page even without doing a scroll ?

I see that there are so many open issues about this : #488 and in react-navigation #10234 ...

Here is the solution I found (as I disabled animationEnabled for both ios/android) : enable for android and "no effect" opacity

<Stack.Navigator
      screenOptions={{
        gestureEnabled: false,
        animationEnabled: Platform.OS === 'android' ? true : false,
        cardStyleInterpolator: ({ current }) => {
          return {
            cardStyle: {
              opacity: 1, // current.progress,
            },
          };
        },
victormak commented 1 year ago

+1, having the same issue, using React native v0.68,any help?

woshi82 commented 1 year ago
  1. in my case , when the parents of ViewPager2 has layout width 0 height 0 .
    ViewPager2 will fire onPageSelected(0).
  2. and in my code , is all of the optimization of react-navigation stack module, will set the prev prev page to display: none. While, this is normal optimization operation .

You can try this solution. hope it help. https://github.com/callstack/react-native-pager-view/compare/master...woshi82:react-native-pager-view:bugfix/onPageSelected

yunzhouhua commented 1 year ago

I have the same issue, my ViewPager has 2 page, when I scroll to Page2, and click a button to navigate to a new screen, then goBack, ViewPager scroll to Page1, not stay in Page2.

I set Screen Options.animationEnabled = true, works for me, this property default value is true in Android and iOS, but someone set false in my project.

Environment

react: 17.0.2 react-native: 0.68.2 react-navigation: 6.3.18 react-native-pager-view: 6.2.1

hjun555 commented 11 months ago

me too. onPageSelected function is called twice in android only. (it called once in ios) even when the "nativeEvent.position" is not changed, onPageSelected called twice. any solution for this?

troZee commented 11 months ago

Could you check, if it exists in this example https://github.com/callstack/react-native-pager-view/blob/master/example/src/OnPageSelectedExample.tsx ?