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'));
              },
            ),
          ),
        ],
      ),
    );
  }
}