我有一个在其主页上使用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页面?
但是,该问题并未令人满意地回答,因为给出的解决方案并未真正“融合”滚动,尽管其行为类似于所描述的行为。它不会自然滚动。
这可以通过使用PageController.postion
属性的drag
方法来实现,该方法在内部拖动ScrollPosition
屏幕的。这样,用户可以像中途拖动一样直观地拖动页面,然后离开或完全继续。
这个想法是从另一篇文章中获得启发的,以使用OverScrollNotification,但是添加了更多的步骤来继续进行直观的拖动。
drag.update
OverscrollNotification方法中的DragUpdateDetails 更新拖动。为了使想法简单,我仅粘贴“选项卡”页面的构建方法。
此 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, () {});
}
因为它是作为OverScrolling的一部分处理的,因此必须进行过度滚动。我觉得这很好,因为用户应该对选项卡的末尾有所了解,否则可能不太直观。但是核心思想是使用
drag
拖动方法。可选地,您可以尝试用手势检测器包装最后的tab元素,也许可以像这样发起拖动onTapDown
。我想我找到了办法。给我一点时间..我会完善和分享.. :)
@MateusFelipe我已经更新了答案。这是我能做的最好的。希望它能对您有所帮助。您仍然可能需要处理更多案件。也许您可以要求用户提供反馈,并确定这种平稳过渡是否对他们有意义。:D
谢啦。我以前也有类似的行为,但您的情况更好。我和您的解决方案都具有的唯一缺陷是,快速滑动后效果不佳。也许您想更新您的飞镖板?
完成..我想说它必须发挥很多才能很好地处理拖动取消。.快速拖动不是我们的解决方案的问题,而是页面视图和tabview本身之间的手势之争..如果它们将api暴露给实际上明确地阻止了页面视图的监听,然后就可以完成。