Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter: Continue scrolling in top Listview when reaching bottom of nested Listview

I wish to create the following: when reaching the top or bottom of the inner Listview, I want to continue scrolling in the top Listview. See GIF:

Gif of what I got so far

An option would be to set the physics of the inner Listview to NeverScrollablePhysics when the bottom is reached (using a Listener to the controller) but then if you would want to scroll up again this wouldn't work.

See below for my code, thanks in advance!

class TestAppHomePage extends StatefulWidget {
@override
TestAppHomePageState createState() => new TestAppHomePageState();
}

class TestAppHomePageState extends State<TestAppHomePage> {


 ScrollController _scrollController = ScrollController();

  @override
  void initState() {
    print('set up');
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: Container(
        color: Colors.green,
        child: ListView(
          primary: false,
          // controller: _scrollController,
          children: <Widget>[topWidget(), topWidget(), topWidget(), topWidget()],
        ),
      ),
    );
  }

  Widget topWidget() {
    return Card(
        color: Colors.purple,
        margin: EdgeInsets.all(16),
        child: Container(
          height: 400,
          child: Column(
            children: <Widget>[
              Container(height: 100, color: Colors.white),
              Expanded(
                  child: ListView(
                      controller: _scrollController,
                      children: List.generate(
                        40,
                        (index) {
                          return someText(index);
                        },
                      ).toList()))
            ],
          ),
        ));
  }

  Widget someText(int i) {
    return Text('text no $i');
  }
}
like image 238
Jan Geleijnse Avatar asked Dec 11 '19 21:12

Jan Geleijnse


1 Answers

I work on the same project as OP, but because nobody has responded yet, I thought I would share my own fiddling, thought procesesses and what I have up to now. I will update this answer

First of all, I tried to prevent creating a solution with a lot of boiler-plate code, but I am afraid I have maybe gone a little bit too far in doing so. I am quite certain my solution is not correct, because the compiler gives me warnings.

Without any further ado, my thoughts were:
1. I needed a way to capture the input of the user in the inner list. therefore, it makes sense to create a custom ScrollPhysics.
2. It is important that the innerlist works normally in all cases. Therefore, it always calls super to handle that.
3. I needed to pass around a function that accepted the input I captured and pretended that exactly that input was passed into the outer list.

The Gif with the result

// imports and stuff..

class CustomScroll extends BouncingScrollPhysics { 
  final Function outerController;
  CustomScroll({this.outerController, ScrollPhysics parent}): super(parent: parent);
  @override
  CustomScroll applyTo(ScrollPhysics ancestor) {
    return CustomScroll(
        outerController: outerController,  parent: buildParent(ancestor));
  }
  @override 
  Simulation createBallisticSimulation(ScrollMetrics position, double velocity) {
    if (position.pixels >= position.maxScrollExtent && velocity >= 0.0) {
      outerController(velocity);
    }
    return super.createBallisticSimulation(position, velocity);
  }
}

class ListInAList extends StatefulWidget {
  createState() => ListInAListState();
}
class ListInAListState extends State<ListInAList> with TickerProviderStateMixin {
  List<ScrollController> innerControllers = List();
  ScrollController outerController = ScrollController();
  final outerPhysics = BouncingScrollPhysics();
  void innerListener(double velocity, ScrollController outerController)
  {

    final sim = outerPhysics.createBallisticSimulation(outerController.position, velocity);
    ScrollActivity _test = BallisticScrollActivity(outerController.position.activity.delegate,sim,this);
    if (_test != null)
    outerController.position.beginActivity(_test);

  }

  Widget build(BuildContext context) {
    return ListView.builder(
        itemBuilder: buildItem, controller: outerController, itemCount: 4, physics: outerPhysics);
  }

  Widget buildItem(BuildContext context, int index) {
    final innerController = ScrollController();
    innerControllers.add(innerController);

    return Column(children: [
      Container(height: 100, color: Colors.blue),
      ListView.builder(
        physics: CustomScroll(outerController: (velocity) => innerListener(velocity, outerController)),
        itemBuilder: buildLoremImpsum,
        controller: innerController,
        itemCount: 20),
    ]);
  }
  Widget buildLoremImpsum(context, index) {
    return Container(
        height: 30,
        color: (index % 2 == 0)
            ? Color.fromRGBO(255, 0, 0, 1)
            : Color.fromRGBO(255, 255, 0, 1));
  }
}

So, basically, only createBallisticSimulation and the function innerListener are the truly interesting parts of the code, everything else is just part of the example.

like image 122
Cgrain Avatar answered Sep 28 '22 05:09

Cgrain