温馨提示:本文翻译自stackoverflow.com,查看原文请点击:flutter - How to "merge" scrolls on a TabBarView inside a PageView?
dart flutter

flutter - 如何在PageView内的TabBarView上“合并”滚动?

发布于 2020-05-30 14:42:40

我有一个在其主页上使用PageView的应用程序。今天,我被分配在这些页面之一中插入TabBarView。问题是,当我在最后一个标签页中的两个标签页之间滚动时,向左滚动不会滚动PageView。

我需要一种在tabbarview的开始或结束时使页面视图的滚动滚动的方法。

我发现了一个与问题相反的问题:TabBarView内的PageView颤动:滚动到页面末尾的下一个选项卡

但是,那里提到的方法不适合我的问题。

我举了一个最小的例子:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(
        title: 'TabBarView inside PageView',
        home: MyHomePage(),
      );
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final PageController _pageController = PageController();

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          title: Text('TabBarView inside PageView'),
        ),
        body: PageView(
          controller: _pageController,
          children: <Widget>[
            Container(color: Colors.red),
            GreenShades(),
            Container(color: Colors.yellow),
          ],
        ),
      );
}

class GreenShades extends StatefulWidget {
  @override
  _GreenShadesState createState() => _GreenShadesState();
}

class _GreenShadesState extends State<GreenShades>
    with SingleTickerProviderStateMixin {
  TabController _tabController;

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

  @override
  Widget build(BuildContext context) => Column(
        children: <Widget>[
          TabBar(
            labelColor: Colors.green,
            indicatorColor: Colors.green,
            controller: _tabController,
            tabs: <Tab>[
              const Tab(text: "Dark"),
              const Tab(text: "Normal"),
              const Tab(text: "Light"),
            ],
          ),
          Expanded(
            child: TabBarView(
              controller: _tabController,
              children: <Widget>[
                Container(color: Colors.green[800]),
                Container(color: Colors.green),
                Container(color: Colors.green[200]),
              ],
            ),
          )
        ],
      );

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

请注意,在此MRE中,如果拖动TabBar可以到达第三页,但是如果拖动TabBarView则不能到达第三页。

我如何实现这种行为?


编辑:

如@Fethi所述,存在类似的问题: 是否可以从TabBarView内容区域滑动到相邻的PageView页面?

但是,该问题并未令人满意地回答,因为给出的解决方案并未真正“融合”滚动,尽管其行为类似于所描述的行为。它不会自然滚动。

查看更多

提问者
Mateus Felipe
被浏览
76
Abhilash Chandran 2020-03-18 01:20

这可以通过使用PageController.postion属性的drag方法来实现,该方法在内部拖动ScrollPosition屏幕的。这样,用户可以像中途拖动一样直观地拖动页面,然后离开或完全继续。

这个想法是从另一篇文章中获得启发的,以使用OverScrollNotification,但是添加了更多的步骤来继续进行直观的拖动。

  1. 用户开始滚动时,请收集DragstartDetail。
  2. 监听OverScrollNotification并开始拖动,并同时使用drag.updateOverscrollNotification方法中的DragUpdateDetails 更新拖动
  3. 在ScrollEndNotification上取消拖动。

为了使想法简单,我仅粘贴“选项卡”页面的构建方法。

dart 垫提供了一个完整的示例

演示

  @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: <Widget>[
        TabBar(
          labelColor: Colors.green,
          indicatorColor: Colors.green,
          controller: _tabController,
          tabs: <Tab>[
            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: <Widget>[
                Container(color: Colors.green[800]),
                Container(color: Colors.green),
                Container(color: Colors.green[200]),
              ],
            ),
          ),
        ),
      ],
    );
  }

旧答案

以上可能无法处理某些极端情况。如果您需要以下代码的更多控制,则可以提供相同的结果,但可以处理UserScrollNotification我要粘贴此内容,因为它对其他想知道使用哪个方向滚动轴的人可能有用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, () {});
              }