Skip to content

NestedScroll 协调问题:内层 NestedSingleChildScrollView 滑到底部后,剩余滑动手势会触发外层 NestedPageView 翻页 #13

Description

@youdianfan

Description(问题描述):
在使用 NestedPageView(外层垂直翻页) + NestedSingleChildScrollView(内层可滚动内容)时,遇到以下问题:

当用户在内层从下向上滚动内容并到达底部时,剩余的滑动手势会透传给外层 NestedPageView,导致页面被切换到下一页。
这种行为会让用户在还没看完内层底部内容时就被强制翻页,体验非常差。


Expected behavior(期望的行为):
当用户在内层滚动到底部时,本次连续触摸手势应该只停留在内层,即使滑动已经到边界,也不应该立即交给外层 PageView
只有当用户松手并发起新的手势时,外层 PageView 才能接管滚动。


Actual behavior(实际行为):
内层滚动到底部后,当前触摸的剩余滑动距离会立即触发外层 PageView 翻页。


Steps to reproduce(复现步骤):

  1. 外层使用垂直 NestedPageView,内层是 NestedSingleChildScrollView
  2. 在内层快速从下向上滑动直到到达底部。
  3. 内层到底部后,剩余手势会触发外层 PageView 翻页。

Minimal reproducible code(最小复现代码):

import 'package:flutter/material.dart';
import 'package:nested_scroll_views/material.dart';

class NestedDemo extends StatefulWidget {
  const NestedDemo({super.key});

  @override
  State<NestedDemo> createState() => _NestedDemoState();
}

class _NestedDemoState extends State<NestedDemo> with TickerProviderStateMixin {
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 3, vsync: this);
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: const Icon(Icons.arrow_back),
          onPressed: () {
            Navigator.of(context).pop();
          },
        ),
        title: const Text('Nested Demo'),
      ),
      body: Container(
        width: double.infinity,
        height: MediaQuery.of(context).size.height,
        child: NestedPageView.builder(
          scrollDirection: Axis.vertical,
          itemCount: 10,
          itemBuilder: (BuildContext context, int index) {
            return Container(
              color: Colors.white,
              child: Column(
                children: [
                  Container(
                    color: Colors.red,
                    height: 60,
                    child: TabBar(
                      controller: _tabController,
                      tabs: const [
                        Tab(text: 'Tab 1'),
                        Tab(text: 'Tab 2'),
                        Tab(text: 'Tab 3'),
                      ],
                    ),
                  ),
                  Expanded(
                    child: TabBarView(
                      controller: _tabController,
                      children: [
                        NestedSingleChildScrollView(
                          child: Column(
                            children: [
                              for (int i = 0; i < 7; i++)
                                Container(
                                  color: Colors
                                      .primaries[i % Colors.primaries.length],
                                  height: 100,
                                  margin: const EdgeInsets.all(10),
                                ),
                            ],
                          ),
                        ),
                        Container(
                          color: Colors.yellow,
                        ),
                        Container(
                          color: Colors.grey,
                        ),
                      ],
                    ),
                  )
                ],
              ),
            );
          },
        ),
      ),
    );
  }
}

Environment(环境信息):

  • Flutter version: [3.24.3]
  • nested_scroll_views version: [0.0.12]
  • Platform: iOS / Android

Additional context(补充说明):

  • 期望在滚动协调上能提供一个机制,避免“本次手势”中内层到达边界后立刻交给外层。
  • 类似的需求场景是:NestedScroll 需要区分“手势已结束” vs “当前手势仍在进行中”。

2025-08-10_173548_245.mp4

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions