Flutter 颤振页面视图中的嵌套滚动
我有一些带有Flutter 颤振页面视图中的嵌套滚动,flutter,scroll,uipageviewcontroller,flutter-pageview,Flutter,Scroll,Uipageviewcontroller,Flutter Pageview,我有一些带有PageView小部件的简单页面,里面有ListView。滚动PageView将不起作用。原因很简单。因为嵌套子级使用了指针事件 @override Widget build(BuildContext context) { setupController(); return PageView( controller: controllerPage, scrollDirection: Axis.vertical, child
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),
],
);
}
我最近陷入了一个类似的场景中,在研究和阅读了《颤振》中手势是如何在引擎盖下工作的之后,我找到了一个足够好的工作解决方案。 因此,我使用了RawGestureDetector,它为您提供了一个处理手势的低级小部件,并通过将页面视图和列表视图的
物理设置为而禁用了它们的滚动。这意味着使用各自的控制器滚动这些小部件-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'));
},
),
),
],
),
);
}
}
void main()=>runApp(App());
类应用程序扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料应用(主:脚手架(appBar:appBar(),主体:NestedContainerWidget());
}
}
类NestedContainerWidget扩展StatefulWidget{
@凌驾
_NestedContainerWidgetState createState()=>_NestedContainerWidgetState();
}
类_NestedContainerWidgetState扩展状态{
页面控制器_页面控制器;
ScrollController\u listScrollController;
ScrollController\u activeScrollController;
拖拽;
@凌驾
void initState(){
super.initState();
_pageController=pageController();
_listScrollController=ScrollController();
}
@凌驾
无效处置(){
_pageController.dispose();
_dispose();
super.dispose();
}
void_handleDragStart(dragstart详细信息){
如果(_listScrollController.hasClient&&
_listScrollController.position.context.storageContext!=null){
最终渲染器渲染器渲染器=
_listScrollController.position.context.storageContext.FindEnderObject();
如果(renderBox.paintBounds
.shift(renderBox.localToGlobal(Offset.zero))
.包含(详细信息.全局位置)){
_activeScrollController=\u listScrollController;
_drag=\u activeScrollController.position.drag(详细信息,\u disposeDrag);
返回;
}
}
_activeScrollController=\u页面控制器;
_拖动=_pageController.position.drag(详细信息,_disposeDrag);
}
void\u handleDragUpdate(DragUpdate详细信息){
如果(\u activeScrollController==\u listScrollController&&
details.primaryDelta<0&&
_activeScrollController.position.pixels==
_activeScrollController.position.maxScrollExtent){
_activeScrollController=\u页面控制器;
_拖动?.cancel();
_拖动=_pageController.position.drag(
DragStartDetails(
globalPosition:details.globalPosition,localPosition:details.localPosition),
_丢弃的垃圾袋);
}
_拖动?更新(详细信息);
}
void_handleDragEnd(图纸详图){
_拖动?结束(细节);
}
void_handleDragCancel(){
_拖动?.cancel();
}
void _disposeDrag(){
_拖动=空;
}
@凌驾
小部件构建(构建上下文){
返回速度检测器(
手势:{
垂直排水器:
手势识别器FactoryWithHandlers(
()=>VerticalDragGestureRecognizer(),(VerticalDragGestureRecognizer实例){
实例
…onStart=\u handleDragStart
..onUpdate=\u handleDragUpdate
…onEnd=_handleDragEnd
..onCancel=_handleDragCancel;
})
},
行为:HitTestBehavior.不透明,
子:页面视图(
控制器:_pageController,
滚动方向:轴垂直,
物理学:const NeverScrollableScrollPhysics(),
儿童:[
居中(子项:文本(“第1页”),
列表视图(
控制器:\ u listScrollController,
物理学:const NeverScrollableScrollPhysics(),
子项:List.generate(
20,
(国际索引){
返回列表(标题:文本('Item$index'));
},
),
),
],
),
);
}
}
查看\u handleDragStart
和\u handledragsupdate
中的逻辑,它根据列表视图
小部件的状态及其滚动状态确定应滚动的控制器
我写了一篇关于这个问题的详细文章,你可以在这里查看-
解决方案可以改进,因此输入将非常受欢迎。我曾经帮助我解决同样的问题
基本上,您可以使用NotificationListener
来侦听可滚动子对象的OverscrollNotification
,并使用其控制器滚动父对象
代码:
final pageCntrler=PageController();
_scrollDown()异步{
等待pageCntrler.nextPage(
持续时间:滚动持续时间,
曲线:滚动曲线,
);
}
_滚动()异步{
等待pageCntrler.previousPage(
持续时间:滚动持续时间,
曲线:滚动曲线,
);
}
@凌驾
小部件构建(构建上下文){
返回页面视图(
控制器:寻呼机,
滚动方向:轴垂直,
物理学:NeverscrollableScroll物理学(),
儿童:[
通知侦听器(
通知:(通知){
如果(通知为超限通知){
如果(notification.overscroll>0){
_向下滚动();
}否则{
_滚动();
}
}
},
子项:SingleChildScrollView(),
)
],
);
}
那么,你是想同时在PageView和ListView上滚动吗?@AlaricJamesHartsock不,我想在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'));
},
),
),
],
),
);
}
}