How to “merge” scrolls on a TabBarView inside a PageView?

后端 未结 2 1408
傲寒
傲寒 2021-01-03 15:58

I have an app that uses a PageView on its main page. Today, I got assigned to insert a TabBarView in one of these pages. The problem is that when I scroll the between the ta

2条回答
  •  日久生厌
    2021-01-03 16:34

    This is possible by using the PageController.postion attribute's drag method, which internally drags the ScrollPosition of the screen. This way, user can intuitively drag the pages like drag halfway and then leave or continue fully.

    The idea is inspired from the other post to use the OverScrollNotification but add rather more step to continue intuitive dragging.

    1. Collect the DragstartDetail when user starts scrolling.
    2. Listen for OverScrollNotification and start the draging and at the same time update the drag using the drag.update with the DragUpdateDetails from OverscrollNotification method.
    3. On ScrollEndNotification cancel the the drag.

    To keep the idea simple I am pasting only build method of the Tabs page.

    A fully working example is available in this dart pad.

      @override
      Widget build(BuildContext context) {
        // Local dragStartDetail.
        DragStartDetails dragStartDetails;
        // Current drag instance - should be instantiated on overscroll and updated alongside.
        Drag drag;
        return Column(
          children: [
            TabBar(
              labelColor: Colors.green,
              indicatorColor: Colors.green,
              controller: _tabController,
              tabs: [
                const Tab(text: "Dark"),
                const Tab(text: "Normal"),
                const Tab(text: "Light"),
              ],
            ),
            Expanded(
              child: NotificationListener(
                onNotification: (notification) {
                  if (notification is ScrollStartNotification) {
                    dragStartDetails = notification.dragDetails;
                  }
                  if (notification is OverscrollNotification) {
                    drag = _pageController.position.drag(dragStartDetails, () {});
                    drag.update(notification.dragDetails);
                  }
                  if (notification is ScrollEndNotification) {
                    drag?.cancel();
                  }
                  return true;
                },
                child: TabBarView(
                  controller: _tabController,
                  children: [
                    Container(color: Colors.green[800]),
                    Container(color: Colors.green),
                    Container(color: Colors.green[200]),
                  ],
                ),
              ),
            ),
          ],
        );
      }
    

    Old Answer

    The above might not handle some edge cases. If you need more control below code provides the same result but you can handle UserScrollNotification. I am pasting this because, it might be useful for others who would like to know which direction the use is scrolling w.r.t the Axis of the ScrollView.

                  if (notification is ScrollStartNotification) {
                    dragStartDetails = notification.dragDetails;
                  }
    
                  if (notification is UserScrollNotification &&
                      notification.direction == ScrollDirection.forward &&
                      !_tabController.indexIsChanging &&
                      dragStartDetails != null &&
                      _tabController.index == 0) {
                    _pageController.position.drag(dragStartDetails, () {});
                  }
    
                  // Simialrly Handle the last tab.
                  if (notification is UserScrollNotification &&
                      notification.direction == ScrollDirection.reverse &&
                      !_tabController.indexIsChanging &&
                      dragStartDetails != null &&
                      _tabController.index == _tabController.length - 1) {
                    _pageController.position.drag(dragStartDetails, () {});
                  }
    

提交回复
热议问题