颤振:到达嵌套水平滚动的结尾时滚动到下一页

时间:2019-09-08 18:20:39

标签: flutter

我们目前正在Flutter中构建一个应用程序,该应用程序具有可以在其中滑动的一系列页面,并且在每个页面内部我们都有一个水平滚动。

我们希望这样做,以便一旦到达水平滚动的末尾,就可以通过在水平滚动内部进一步滚动来滚动/滑动到下一页。

以下是展示所需行为的视频: https://vimeo.com/358643279

我们当前的实现存在问题,如下所示。 它在每页中都包含一个PageView,在每页中我们都包含一个ListView用于水平滚动。

一旦我们到达水平滚动的末尾,就不允许您进一步滚动滚动到下一页。

要滚动到下一页,必须在水平滚动之外做出滚动手势才能注册,这是不理想的。

这是一个最小的示例,您应该可以复制和粘贴。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'ScrollingDemo',
        home: PageView(scrollDirection: Axis.horizontal, children: [
          Page(title: "Page 1"),
          Page(title: "Page 2")
        ]));
  }
}

class Page extends StatelessWidget {
  Page({@required this.title});

  final String title;

  @override
  Widget build(BuildContext context) {
    final listView = ListView(
      scrollDirection: Axis.horizontal,
      children: <Widget>[
        _buildContainer(1),
        _buildContainer(2),
        _buildContainer(3),
        _buildContainer(4),
        _buildContainer(5)
      ],
    );

    return Column(
      children: [
        Text(title),
        Container(height: 200, width: 500, child: listView)
      ],
    );
  }

  Container _buildContainer(int index) {
    return Container(
      color: Colors.green,
      width: 250,
      height: 250,
      child: Center(child: Text(index.toString())),
    );
  }
}

非常感谢!

1 个答案:

答案 0 :(得分:0)

您的问题归因于滚动的工作方式。您有两个水平滚动小部件,一个在另一个顶部(ListView在PageView顶部),因此ListView阻止滚动效果到达PageView。您可以使用ScrollControllerPageController解决此问题。将单独的ScrollController添加到ListView中,并在这些控制器中侦听滚动事件,然后在PageView上相应地设置PageController的页面。

_scrollListener() async {
    ScrollPosition position = _controller.position;
    if (_controller.offset == position.maxScrollExtent) {
      widget.pageController.nextPage(duration: Duration(milliseconds: 100), curve: Curves.linear);
    } else if (_controller.offset == position.minScrollExtent) {
      widget.pageController.previousPage(duration: Duration(milliseconds: 100), curve: Curves.linear);
    }
  }

请注意,您也必须切换到StatefulWidget Page类以实现此目的,但这非常简单。这段代码可以解决这个问题:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  final PageController _pageController = PageController();
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'ScrollingDemo',
        home: PageView(controller: _pageController, scrollDirection: Axis.horizontal, children: [
          Page(
            title: "Page 1",
            pageController: _pageController,
          ),
          Page(title: "Page 2", pageController: _pageController)
        ]));
  }
}

class Page extends StatefulWidget {
  final PageController pageController;
  final String title;
  Page({@required this.title, @required this.pageController});

  @override
  PageState createState() {
    return PageState();
  }
}

class PageState extends State<Page> {
  final ScrollController _controller = ScrollController();

  @override
  void initState() {
    super.initState();
    _controller.addListener(_scrollListener);
  }

  _scrollListener() async {
    ScrollPosition position = _controller.position;
    if (_controller.offset == position.maxScrollExtent) {
      widget.pageController.nextPage(duration: Duration(milliseconds: 100), curve: Curves.linear);
    } else if (_controller.offset == position.minScrollExtent) {
      widget.pageController.previousPage(duration: Duration(milliseconds: 100), curve: Curves.linear);
    }
  }

  @override
  Widget build(BuildContext context) {
    final listView = ListView(
      controller: _controller,
      scrollDirection: Axis.horizontal,
      children: <Widget>[_buildContainer(1), _buildContainer(2), _buildContainer(3), _buildContainer(4), _buildContainer(5)],
    );

    return Column(
      children: [Text(widget.title), Container(height: 200, width: 500, child: listView)],
    );
  }

  Container _buildContainer(int index) {
    return Container(
      color: Colors.green,
      width: 250,
      height: 250,
      child: Center(child: Text(index.toString())),
    );
  }
}