简体   繁体   English

Flutter 垂直可滚动的listView在垂直可滚动的pageView中

[英]Flutter vertical scrollable listView in vertical scrollable pageView

Looking for help on this one, it seemed simple to at first but couldn't figure it out...寻求帮助,起初似乎很简单,但无法弄清楚......

Basically I'm trying to make a page with a vertical scrollable listView inside a vertical scrollable pageView.基本上,我正在尝试在垂直可滚动的 pageView 中创建一个具有垂直可滚动列表视图的页面。 For example the list is on page 2, the user is on page one and scrolls down to page 2. Now he scrolls down until the end of the list and I want his to be able to scroll a bit more that it would go to page 3.例如列表在第 2 页,用户在第一页并向下滚动到第 2 页。现在他向下滚动到列表的末尾,我希望他能够滚动更多,而不是 go 到页面3.

Any suggestions on how to implement this?关于如何实现这一点的任何建议?

What works for me is that we need to attach the ListView's scroll controller to listen to the scroll position.对我有用的是,我们需要附加 ListView 的滚动条 controller 来收听滚动条 position。 The page controller will then move to either next or previous page.然后页面 controller 将移动到下一页或上一页。

PageController pageController;
ScrollController scrollController;

@override
void initState() {
  super.initState();

  pageController = PageController();
  scrollController = ScrollController();

  scrollController.addListener(() {
    // listView reaches the bottom
    if (scrollController.offset >= scrollController.position.maxScrollExtent - 100 &&
      !scrollController.position.outOfRange) {
      pageController.nextPage(duration: const Duration(seconds: 1), curve: ElasticOutCurve());
    }

    // listView reaches the top
    if (scrollController.offset <= scrollController.position.minScrollExtent &&
        !scrollController.position.outOfRange) {
      pageController.previousPage(duration: const Duration(seconds:1), curve: ElasticOutCurve());
    }
  });
}

Don't forget to attach the controllers不要忘记连接控制器

PageView(
  controller: pageController,
  ...


ListView(
  controller: scrollController,
  ...

This GitHub issue has a handy solution that I think has the "hover" behavior between each page that you need.这个 GitHub问题有一个方便的解决方案,我认为它在您需要的每个页面之间具有“悬停”行为。 Here is the sample code provided within the thread:这是线程中提供的示例代码:

void main() => runApp(App());

class App extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: Example()
      )
    );
  }

}

class Example extends StatefulWidget {

  @override
  _ExampleState createState() => _ExampleState();

}

class _ExampleState extends State<Example> {

  PageController _pageController;
  ScrollController _listScrollController;
  ScrollController _activeScrollController;
  Drag _drag;

  @override
  void initState() {
    super.initState();
    _pageController = PageController();
    _listScrollController = ScrollController();
  }

  @override
  void dispose() {
    _pageController.dispose();
    _listScrollController.dispose();
    super.dispose();
  }

  void _handleDragStart(DragStartDetails details) {
    if (_listScrollController.hasClients && _listScrollController.position.context.storageContext != null) {
      final RenderBox renderBox = _listScrollController.position.context.storageContext.findRenderObject();
      if (renderBox.paintBounds.shift(renderBox.localToGlobal(Offset.zero)).contains(details.globalPosition)) {
        _activeScrollController = _listScrollController;
        _drag = _activeScrollController.position.drag(details, _disposeDrag);
        return;
      }
    }
    _activeScrollController = _pageController;
    _drag = _pageController.position.drag(details, _disposeDrag);
  }

  void _handleDragUpdate(DragUpdateDetails details) {
    if (_activeScrollController == _listScrollController && details.primaryDelta < 0 && _activeScrollController.position.pixels == _activeScrollController.position.maxScrollExtent) {
      _activeScrollController = _pageController;
      _drag?.cancel();
      _drag = _pageController.position.drag(
        DragStartDetails(
          globalPosition: details.globalPosition,
          localPosition: details.localPosition
        ),
        _disposeDrag
      );
    }
    _drag?.update(details);
  }

  void _handleDragEnd(DragEndDetails details) {
    _drag?.end(details);
  }

  void _handleDragCancel() {
    _drag?.cancel();
  }

  void _disposeDrag() {
    _drag = null;
  }

  @override
  Widget build(BuildContext context) {
    return RawGestureDetector(
      gestures: <Type, GestureRecognizerFactory>{
          VerticalDragGestureRecognizer: GestureRecognizerFactoryWithHandlers<VerticalDragGestureRecognizer>(
          () => VerticalDragGestureRecognizer(),
          (VerticalDragGestureRecognizer instance) {
            instance
              ..onStart = _handleDragStart
              ..onUpdate = _handleDragUpdate
              ..onEnd = _handleDragEnd
              ..onCancel = _handleDragCancel;
          }
          )
      },
      behavior: HitTestBehavior.opaque,
      child: PageView(
        controller: _pageController,
        scrollDirection: Axis.vertical,
        physics: const NeverScrollableScrollPhysics(),
        children: [
          ListView(
            controller: _listScrollController,
            physics: const NeverScrollableScrollPhysics(),
            children: List.generate(
              25,
              (int index) {
                return ListTile(
                  title: Text('Item $index')
                );
              }
            )
          ),
          Center(
            child: Text('Page 2')
          )
        ],
      )
    );
  }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM