A recent project to implement an infinite loop of PageView, the main idea is to initialize the list of PageView by adding an end and beginning widget at the beginning and end, when sliding to the start and end of the page manually switch, detailed search PageView infinite rotation. This approach has a point is to maintain the two indexes, a is the index to the internal list, one is the index of external show, due to the capacity of the list is more than display the number of 2, so if you want to in some such as external indicator or synchronous display timer function to and page or switching operation on the page, The displayed index needs to be converted to the index of the list. However, online said are some of the more simple implementation, see more is when the slide to manual switch when a delay, so that you can avoid the direct switch page caused by the jam and beat phenomenon. But there is a problem, if you want to implement a follow page switch indicator at the same time, will appear when the page after the switch indicator will follow in the past, because the page performs a time delay switch, and after the delay will truly change index, the setstate, indicator to response to the index after the switch, However, if the switch is made before the delay, the indicator will go first. So there are some problems with this approach. So the key to solve this problem lies in the judgment of how to switch pages. There are two ways to do this. The first way is to implement onPagechanged method in viewPage, perform a logical judgment inside, and then skip to the page with Controller. However, this method exists and onPagechanged will be called back when controller skips. As a result, multiple unnecessary operations on the index can occur, and additional features such as timers can make it difficult to separate page logic, still fail to address indicator latency, and make fine-grained operations difficult. The second way we are going to look at the source of PageView, from the source point of view to solve the problem is the right way. First let’s click on the source of PageView

// create pageView (); // create pageView (); I wrote some parse@override Widget Build (BuildContext Context) {final AxisDirection AxisDirection = _getDirection(context); final ScrollPhysics physics = _ForceImplicitScrollPhysics( allowImplicitScrolling: widget.allowImplicitScrolling, ).applyTo(widget.pageSnapping ? _kPagePhysics.applyTo(widget.physics) : widget.physics); // A NotificationListener is used to monitor pageView. // A NotificationListener is used to monitor pageView. return NotificationListener<ScrollNotification>( onNotification: (ScrollNotification notification) {// There are two points to note here. One is ScrollUpdateNotification, which means that the page has been swiped. If (notification.depth == 0 && Widget.onPagechanged!) = null && notification is ScrollUpdateNotification) { final PageMetrics metrics = notification.metrics as PageMetrics; final int currentPage = metrics.page.round(); if (currentPage ! = _lastReportedPage) { _lastReportedPage = currentPage; OnPageChanged = onPageChanged = onPageChanged = onPageChanged = onPageChanged widget.onPageChanged(currentPage); } } return false; }, // pageView can be seen as an external listener event, inside which the page switch logic is implemented through Scrollable //Scrollable internal is actually implemented through RawGestureDetector gesture listening, here is not much expanded, You can look at the source code. child: Scrollable( dragStartBehavior: widget.dragStartBehavior, axisDirection: axisDirection, controller: widget.controller, physics: physics, restorationId: widget.restorationId, viewportBuilder: (BuildContext context, ViewportOffset position)Copy the code

The difficulty was that we rewrote the onPagechanged method so that the problem could not be solved properly. Now we have found the onPagechanged call and we just need to find a way to avoid it. Of course, we are going to talk about NotificationListener and the corresponding bubble event transmission mechanism of flutter. You can read this article here. To summarize, flutter has an event rule called bubble transfer for the notification component. If the underlying Notification returns false in its onNotification logic and is not root, It traverses up to find its ancestor Notification component until it encounters root or one returns true and passes the event. And in onNotification you can listen for and handle a variety of events, so we can put the logic for the viewPage jump and the index in here, and we can do fine grained logic for things like slide start and end, This allows operations and other functions to be implemented externally.

// This can be written like this, Indexes of judging the end events page and switch at the same time in the update event for our index the if (notification is ScrollStartNotification && widget. OnPageStartChanged! = null) { widget.onPageStartChanged(_currentReportedPage); } else if (notification is ScrollEndNotification && widget.onPageEndChanged ! = null) { widget.onPageEndChanged(_currentReportedPage);Copy the code

So not only can the infinite wheel event be handled in this way, but other operations can be handled in this way as well, and since we did not pass onPagechanged, there is no problem with multiple calls. Pageview will not go in if onPagechanged is NULL. It will direct the logic of notification that we write outside pageView. The final structure looks something like this

NotificationListener<ScrollNotification>(
      child: ScrollConfiguration(
        behavior: OverScrollBehavior(),
        child: PageView(
          controller: _controller,
          children: _children,
          physics: const ClampingScrollPhysics(),
          allowImplicitScrolling: widget.allowImplicitScrolling,
        ),
      ),
      onNotification: (notification) {
Copy the code