flutter 嵌套页面视图:当内部PageView到达末尾时更改外部PageView

rhfm7lfc  于 2023-05-23  发布在  Flutter
关注(0)|答案(2)|浏览(294)

考虑一个PageView用法,其中一个PageView位于另一个PageView中:

PageView(
  children: [
    Container(color: Colors.red),
    Column(
      children: [
        Text('Title'),
        PageView(
          children: [
            Container(color: Colors.green),
            Container(color: Colors.yellow),
          ],
        ),
      ],
    )
  ],
);

这样的架构将允许我们在外部PageView和内部PageView之间转换。但是一旦我们到达了内在的PageView,我们就再也出不去了。
这里有一个gif展示它:

在这样的架构中,我们不能将两个PageView“合并”成一个,因为内部PageView不会填满屏幕
我们如何才能实现类似的结果,同时允许从两个PageView转换而不会卡住?

von4xj4u

von4xj4u1#

我有一个使用NotificationListener的解决方案,即用PageController控制页面视图。用户实际上控制较低的PageView并将偏移量传递给较高的PageView。

final _pageController1 = PageController(); // Controlled
final _pageController2 = PageController(); // Controller

@override
Widget build(BuildContext context) {
  var leftOverScroll = 0.0; // total over-scroll offset on left side
  var rightOverScroll = 0.0;

  return PageView(
    physics: const ClampingScrollPhysics(),
    controller: _pageController1,
    children: [
      Container(color: Colors.red),
      Column(
        children: [
          const Text('Title'),
          Expanded(
            child: NotificationListener(
              onNotification: (notification) {
                // over scroll to the left side
                if (notification is OverscrollNotification &&
                    notification.overscroll < 0) {
                  leftOverScroll += notification.overscroll;
                  _pageController1.position.correctPixels(
                      _pageController1.position.pixels +
                          notification.overscroll);
                  _pageController1.position.notifyListeners();
                }

                // scroll back after left over scrolling
                if (leftOverScroll < 0) {
                  if (notification is ScrollUpdateNotification) {
                    final newOverScroll = math.min(
                        notification.metrics.pixels + leftOverScroll, 0.0);
                    final diff = newOverScroll - leftOverScroll;
                    _pageController1.position.correctPixels(
                        _pageController1.position.pixels + diff);
                    _pageController1.position.notifyListeners();
                    leftOverScroll = newOverScroll;
                    _pageController2.position.correctPixels(0);
                    _pageController2.position.notifyListeners();
                  }
                }

                // release left
                if (notification is UserScrollNotification &&
                    notification.direction == ScrollDirection.idle &&
                    leftOverScroll != 0) {
                  _pageController1.previousPage(
                      curve: Curves.ease,
                      duration: const Duration(milliseconds: 400));
                  leftOverScroll = 0;
                }

                // over scroll to the right side
                if (notification is OverscrollNotification &&
                    notification.overscroll > 0) {
                  rightOverScroll += notification.overscroll;
                  _pageController1.position.correctPixels(
                      _pageController1.position.pixels +
                          notification.overscroll);
                  _pageController1.position.notifyListeners();
                }

                // scroll back after right over scrolling
                if (rightOverScroll > 0) {
                  if (notification is ScrollUpdateNotification) {
                    final maxScrollExtent =
                        notification.metrics.maxScrollExtent;
                    final newOverScroll = math.max(
                        notification.metrics.pixels +
                            rightOverScroll -
                            maxScrollExtent,
                        0.0);
                    final diff = newOverScroll - rightOverScroll;
                    _pageController1.position.correctPixels(
                        _pageController1.position.pixels + diff);
                    _pageController1.position.notifyListeners();
                    rightOverScroll = newOverScroll;
                    _pageController2.position.correctPixels(maxScrollExtent);
                    _pageController2.position.notifyListeners();
                  }
                }

                // release right
                if (notification is UserScrollNotification &&
                    notification.direction == ScrollDirection.idle &&
                    rightOverScroll != 0) {
                  _pageController1.nextPage(
                      curve: Curves.ease,
                      duration: const Duration(milliseconds: 400));
                  rightOverScroll = 0;
                }

                return false;
              },
              child: PageView(
                physics: ClampingScrollPhysics(),
                controller: _pageController2,
                children: [
                  Container(color: Colors.green),
                  Container(color: Colors.yellow),
                ],
              ),
            ),
          ),
        ],
      ),
      Container(color: Colors.blue),
    ],
  );
}

对于如何避免来自其他子部件的通知,我只能想到用另一个NotificationListener Package 它

NotificationListener(
  onNotification: (_)=> true,
  child: ListView.builder(
    scrollDirection: Axis.horizontal,
    itemBuilder: (ctx,index) => Container(width: 50,color: Colors.primaries[index%Colors.primaries.length],),
  ),
),

结果:

最后:

  • 我只处理PageView的左侧滚动
  • 为了简单起见,我没有在拖动结束时(根据速度和页面位置计算)实现PageScrollPhysic。
6mzjoqzu

6mzjoqzu2#

一个简单的方法是使用Notification listener和animateToPage:

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Container(
      color: Colors.yellow,
      child: PageView(
        controller: _parentController,
        children: [
          Container(
            color: Colors.green,
            child: Center(
              child: Text('0'),
            ),
          ),
          NotificationListener<OverscrollNotification>(
            onNotification: (notification) {
              if (notification.overscroll < 0)
                _parentController.animateToPage(0,
                    duration: Duration(milliseconds: 400),
                    curve: Curves.easeInOutQuart);
              if (notification.overscroll > 0)
                _parentController.animateToPage(2,
                    duration: Duration(milliseconds: 400),
                    curve: Curves.easeInOutQuart);
              return false;
            },
            child: PageView(
              children: [
                Container(
                  color: Colors.green,
                  child: Center(child: Text('2: 0')),
                ),
                Container(
                  color: Colors.blue,
                  child: Center(child: Text('2: 1')),
                ),
              ],
            ),
          ),
          Container(
            color: Colors.red,
            child: Center(
              child: Text('2'),
            ),
          ),
        ],
      ),
    ),
  );
}

相关问题