简体   繁体   中英

how to sync scroll of singlechildscrollview and listview flutter?

How to combine scroll of singlechildscrollview and listview? I am trying to create a layout in which there is a singlechildscrollview containing column which wraps some widgets and a listview. How do I combine the scroll controller of the scroll view and the listview. (ie I want the listview to scroll only when the other widgets are scrolled out and out of display and again show only when the listview is reached top.) For instance: in instagram app you have the widgets like userimage, username, followers count, following count, etc and then there is a listview listing the posts of the user. The scroll is connected. How do i achieve this???

What I Want

我想要的是

My previous code

我之前的代码

After Implementing Andrey Turkovsky's Code

实施安德烈·特科夫斯基的代码后

I can write solution for scrolling parent when nested listview is reaches top. Here is part of me code - I have ListView with 2 elements. And 2nd is another 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: ...,
      );
    }
  }

UPD Added solution for changing scrolling physics of child with stream

I build my Listview, like so:

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
        },
    ),

And then I build my 'TileWidget', like so:

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

It's not very pretty. But it is simple and it works.

This might be a little late, but you can add NeverScrollableScrollPhysics() to the physics param of the ListView like this:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM