扑。 PageView 内的嵌套滚动
Posted
技术标签:
【中文标题】扑。 PageView 内的嵌套滚动【英文标题】:Flutter. Nested scroll inside PageView 【发布时间】:2019-12-03 20:48:53 【问题描述】:我有带有PageView
小部件的简单页面,里面有ListView
。滚动PageView
不起作用。原因很简单。因为嵌套子级消耗的指针事件。
@override
Widget build(BuildContext context)
setupController();
return PageView(
controller: controllerPage,
scrollDirection: Axis.vertical,
children: <Widget>[
ListView.builder(
controller: controller,
padding: EdgeInsets.all(AppDimens.bounds),
itemCount: 15,
itemBuilder: (context, index)
return Container(
height: 100,
color: index %2 == 0
? Colors.amber : Colors.blueAccent,
);
,
),
Container(color: Colors.green),
Container(color: Colors.blue),
],
);
我的问题是有什么理智的方法可以让它一起工作吗?您可能会看到PageView
的垂直轴,但使用PageView
的水平轴会出现完全相同的问题和水平的ListView
。
到目前为止我尝试了什么?我有一些解决方法。即使它并不复杂,只是感觉不太好和笨重。通过使用AbsorbPointer
和自定义控制器进行滚动。
final controller = ScrollController();
final controllerPage = PageController(keepPage: true);
bool hasNestedScroll = true;
void setupController()
controller.addListener(()
if (controller.offset + 5 > controller.position.maxScrollExtent &&
!controller.position.outOfRange)
/// Swap to Inactive, if it was not
if (hasNestedScroll)
setState(()
hasNestedScroll = false;
);
else
/// Swap to Active, if it was not
if (!hasNestedScroll)
setState(()
hasNestedScroll = true;
);
);
controllerPage.addListener(()
if (controllerPage.page == 0)
setState(()
hasNestedScroll = true;
);
);
@override
Widget build(BuildContext context)
setupController();
return PageView(
controller: controllerPage,
scrollDirection: Axis.vertical,
children: <Widget>[
AbsorbPointer(
absorbing: !hasNestedScroll,
child: ListView.builder(
controller: controller,
padding: EdgeInsets.all(AppDimens.bounds),
itemCount: 15,
itemBuilder: (context, index)
return Container(
height: 100,
color: index %2 == 0
? Colors.amber : Colors.blueAccent,
);
,
),
),
Container(color: Colors.green),
Container(color: Colors.blue),
],
);
【问题讨论】:
那么您是否要同时在 PageView 和 ListView 上滚动? @AlaricJamesHartsock 不,我正在尝试滚动ListView
,只要它有空间。一旦 ListView 的滚动到达终点,我需要滚动 PageView
@AlaricJamesHartsock 你有什么建议吗?
对不起,我被打败了。也许尝试查看 API,看看您是否可以手动放弃从列表视图到页面视图的滚动控制,一旦它一直向下滚动。我希望你能找到你要找的东西。
【参考方案1】:
我最近陷入了类似的情况,经过一些研究和阅读有关手势如何在 Flutter 中工作的信息后,我找到了一个足够好的工作解决方案。
所以我使用了 RawGestureDetector,它为您提供了一个低级别的手势小部件处理,并通过将 PageView 和 ListView 的 physics
设置为 NeverScrollableScrollPhysics
来禁用滚动。这意味着使用它们各自的控制器滚动这些小部件 - PageViewController
和 ScrollController
。现在根据 ListView 的位置和状态,使用控制器选择并拖动两者中的一个活动控制器。主类看起来像这样。
void main() => runApp(App());
class App extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(home: Scaffold(appBar: AppBar(), body: NestedContainerWidget()));
class NestedContainerWidget extends StatefulWidget
@override
_NestedContainerWidgetState createState() => _NestedContainerWidgetState();
class _NestedContainerWidgetState extends State<NestedContainerWidget>
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: [
Center(child: Text('Page 1')),
ListView(
controller: _listScrollController,
physics: const NeverScrollableScrollPhysics(),
children: List.generate(
20,
(int index)
return ListTile(title: Text('Item $index'));
,
),
),
],
),
);
查看_handleDragStart
和_handleDragUpdate
中的逻辑,它根据ListView
小部件的状态及其滚动状态确定应该滚动的控制器。
我写了一篇关于这个问题的详细文章,你可以在这里查看-https://medium.com/@Mak95/nested-scrolling-listview-inside-pageview-in-flutter-a57b7a6241b1
解决方案可以改进,因此非常欢迎输入。
【讨论】:
始终为您的问题添加标签,以便正确的人群可以查看。【参考方案2】:我使用this answer 帮助我解决了同样的问题。
基本上,您使用NotificationListener
监听OverscrollNotification
的可滚动子级,并使用其控制器滚动父级。
代码:
final pageCntrler = PageController();
_scrollDown() async
await pageCntrler.nextPage(
duration: scrollDuration,
curve: scrollCurve,
);
_scrollUp() async
await pageCntrler.previousPage(
duration: scrollDuration,
curve: scrollCurve,
);
@override
Widget build(BuildContext context)
return PageView(
controller: pageCntrler,
scrollDirection: Axis.vertical,
physics: NeverScrollableScrollPhysics(),
children: [
NotificationListener(
onNotification: (notification)
if (notification is OverscrollNotification)
if (notification.overscroll > 0)
_scrollDown();
else
_scrollUp();
,
child: SingleChildScrollView(),
)
],
);
【讨论】:
以上是关于扑。 PageView 内的嵌套滚动的主要内容,如果未能解决你的问题,请参考以下文章
cocos studio pageview看不到indicator指示点
嵌套滚动视图内的 Recyclerview 滚动,但不像普通 Recyclerview 或嵌套滚动视图那样快速滚动
Flutter 小技巧之 ListView 和 PageView 的各种花式嵌套