如何同步singlechildscrollview和listview颤振的滚动?

如何同步singlechildscrollview和listview颤振的滚动?,listview,flutter,Listview,Flutter,如何组合singlechildscrollview和listview的滚动?我试图创建一个布局,其中有一个singlechildscrollview,其中包含包装一些小部件的列和一个listview。如何组合滚动视图和列表视图的滚动控制器。(也就是说,我希望listview仅在其他窗口小部件被滚动出屏幕时滚动,并且仅在listview到达顶部时再次显示。)例如:在instagram应用程序中,您拥有userimage、username、followers count、following coun

如何组合singlechildscrollview和listview的滚动?我试图创建一个布局,其中有一个singlechildscrollview,其中包含包装一些小部件的列和一个listview。如何组合滚动视图和列表视图的滚动控制器。(也就是说,我希望listview仅在其他窗口小部件被滚动出屏幕时滚动,并且仅在listview到达顶部时再次显示。)例如:在instagram应用程序中,您拥有userimage、username、followers count、following count等窗口小部件,然后有一个列出用户帖子的listview。卷轴已连接。我如何做到这一点

我想要什么

我以前的代码

在实现Andrey Turkovsky的代码之后

当嵌套listview到达顶部时,我可以编写滚动父级的解决方案。这是me代码的一部分-我有一个包含2个元素的ListView。第二个是另一个列表视图

class _ConferenceScaffoldState extends CommonScaffoldState<ConferenceScaffold> {

    final ScrollController controller = ScrollController();
    final GlobalKey widgetKey = GlobalKey();
    /* widgetKey is for widget in buildHeaderRow() */
    StreamController<bool> _streamController = StreamController<bool>();

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text(conference_title),
          centerTitle: true,
        ),
        body: getBody(),
      );
    }

    Widget getBody() {
    controller.addListener((){
      if (widgetKey.currentContext != null) {
        double height = widgetKey.currentContext.size.height;
        _streamController.add(controller.offset >= height);
      }
    });
      return ListView(
        controller: controller,
        children: <Widget>[buildHeaderRow(), buildPagerRow()],
      );
    }

    Widget buildPagerRow() => _EventSpeakerPager(scrollCallback, _streamController.stream);

    scrollCallback(double position) => controller.position.jumpTo(controller.position.pixels - position);

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

  typedef ScrollCallback = void Function(double position);

  class _EventSpeakerPager extends StatefulWidget {
    _EventSpeakerPager(this.callback, this.stream);

    final ScrollCallback callback;
    final Stream<bool> stream;

    @override
    State<StatefulWidget> createState() => _EventSpeakerPagerState();
  }

  class _EventSpeakerPagerState extends State<_EventSpeakerPager> {
    final GlobalKey tabKey = GlobalKey();
    bool isChildScrollEnabled = false;

  @override
  void initState() {
    super.initState();
    widget.stream.distinct().listen((bool data) {
      setState(() {
        isChildScrollEnabled = data;
      });
    });
  }

    @override
    Widget build(BuildContext context) {
      ListView eventList = ListView.builder(
        physics: isChildScrollEnabled ? AlwaysScrollableScrollPhysics() : NeverScrollableScrollPhysics(),
        controller: ScrollController(),
        itemBuilder: (buildContext, position) {
          if (position.isOdd) return CommonDivider();
          return buildEventRow(getEventList()[position ~/ 2], false, null);
        },
        itemCount: getEventList().length * 2,
      );
      return Listener(
        onPointerMove: (event) {
          double pixels = eventList.controller.position.pixels;
          if (event.delta.dy > 0.0 && pixels == 0.0) widget.callback(event.delta.dy);
        },
        child: ...,
      );
    }
  }
class\u ConferenceScaffoldState扩展CommonCaffoldState{
最终ScrollController=ScrollController();
最终GlobalKey-widgetKey=GlobalKey();
/*widgetKey用于buildHeaderRow()中的小部件*/
StreamController _StreamController=StreamController();
@凌驾
小部件构建(构建上下文){
返回脚手架(
appBar:appBar(
标题:文本(会议标题),
标题:对,
),
body:getBody(),
);
}
小部件getBody(){
controller.addListener(){
if(widgetKey.currentContext!=null){
双重高度=widgetKey.currentContext.size.height;
_streamController.add(controller.offset>=高度);
}
});
返回列表视图(
控制器:控制器,
子项:[buildHeaderRow(),buildPagerRow()],
);
}
Widget buildPagerRow()=>\u eventSpeakerPage(scrollCallback,\u streamController.stream);
scrollCallback(双位置)=>controller.position.jumpTo(controller.position.pixels-position);
@凌驾
无效处置(){
controller.dispose();
super.dispose();
}
}
typedef ScrollCallback=无效函数(双位置);
类_EventSpeakerPager扩展StatefulWidget{
_EventSpeakerPage(this.callback,this.stream);
最终卷轴回调;
最终河流;
@凌驾
State createState()=>\u EventSpeakerPagerState();
}
类_EventSpeakerPagerState扩展状态{
最终的GlobalKey tabKey=GlobalKey();
bool isChildScrollEnabled=false;
@凌驾
void initState(){
super.initState();
widget.stream.distinct().listen((bool数据){
设置状态(){
IsChildScrolEnabled=数据;
});
});
}
@凌驾
小部件构建(构建上下文){
ListView事件列表=ListView.builder(
物理:是否已禁用?始终滚动滚动滚动物理():从不滚动滚动滚动物理(),
控制器:ScrollController(),
itemBuilder:(构建上下文,位置){
if(position.isOdd)返回CommonDivider();
返回buildEventRow(getEventList()[position~/2],false,null);
},
itemCount:getEventList().length*2,
);
返回侦听器(
onPointerMove:(事件){
双像素=eventList.controller.position.pixels;
if(event.delta.dy>0.0&&pixels==0.0)widget.callback(event.delta.dy);
},
孩子:。。。,
);
}
}
UPD
添加了使用stream更改子对象滚动物理的解决方案

我构建了我的Listview,如下所示:

List<String> myList = ["one", "two", "three", "four"]; //Just some example list data

ListView.builder(,
    itemCount: myList.length,
    itemBuilder: (BuildContext context, int index) {
        return TileWidget(
              myList[index],              
              index == 0, //this indicates that it is the first item
              index == myList.length - 1); //this indicates that it is the last item
        },
    ),
class TileWidget extends StatelessWidget {
  TileWidget(this.tileData,  this.isFirstItem, this.isLastItem,);
  String tileData;
  bool isFirstItem;
  bool isLastItem;  

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        isFirstItem ? Text("this is a title") : Container(),
        Text(tileData),
        isLastItem ? Text("This is a footer") : Container(),
      ],
    );
  }
}
List myList=[“一”、“二”、“三”、“四”]//只是一些示例列表数据
ListView.builder(,
itemCount:myList.length,
itemBuilder:(构建上下文,int索引){
返回平铺边(
myList[索引],
index==0,//这表示它是第一项
index==myList.length-1);//这表示它是最后一项
},
),
然后我构建我的“TileWidget”,如下所示:

List<String> myList = ["one", "two", "three", "four"]; //Just some example list data

ListView.builder(,
    itemCount: myList.length,
    itemBuilder: (BuildContext context, int index) {
        return TileWidget(
              myList[index],              
              index == 0, //this indicates that it is the first item
              index == myList.length - 1); //this indicates that it is the last item
        },
    ),
class TileWidget extends StatelessWidget {
  TileWidget(this.tileData,  this.isFirstItem, this.isLastItem,);
  String tileData;
  bool isFirstItem;
  bool isLastItem;  

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        isFirstItem ? Text("this is a title") : Container(),
        Text(tileData),
        isLastItem ? Text("This is a footer") : Container(),
      ],
    );
  }
}
class TileWidget扩展了无状态小部件{
TileWidget(this.tileData,this.isFirstItem,this.isLastItem,);
线状瓦氏体;
布尔是第一项;
布尔群岛;
@凌驾
小部件构建(构建上下文){
返回列(
儿童:[
isFirstItem?文本(“这是一个标题”):Container(),
文本(tileData),
isLastItem?Text(“这是一个页脚”):Container(),
],
);
}
}

不太好看。但是它很简单,而且可以工作。

这可能有点晚了,但是您可以像这样将NeverScrollableScrollPhysics()添加到ListView的物理参数中:

Widget buildLoadedWidget(CarouselCardsLoaded state) {
return ListView.builder(
  shrinkWrap: true,
  physics: NeverScrollableScrollPhysics(),
  itemBuilder: (context, i) {
    return GestureDetector(
      onTap: () async {

你不想创建一个列表视图,其中包含带有小部件的列,然后是你的列表项吗?非常感谢你的回答,但是我是一个新手,我发现很难理解你的示例。你能详细说明一下吗。(ScrollCallback,CommonCaffoldState with TickerProviderStateMixin,CommonListState)
ScrollCallback
-仅是带有一个双参数(位置)的函数的别名,
with TickerProviderStateMixin
-这对于不同的动画控制器是必要的,但我认为您可以删除它(我只跳过了代码中必要的一部分). 和
CommonListState
-抱歉,我错过了它-这是我的抽象类,但是你可以使用普通的
State
请检查gifs@Andrey turkovsky,现在获取你的解决方案-我已经更新了代码。将widgetKey添加到子listview上方的列中。我想你会理解另一个