Jamshed Alam
Jamshed Alam

Reputation: 12854

Flutter Nested list scroll parent when reach to end/start of inner list

I am implementing a nested list in Flutter where need to start scrolling the parent list when it reach to the end/start the inner list. I tried with several ways, no one lucks. This one is the last approach i tried with.

This works for top to bottom , but when i scroll bottom to top, it does not(parent-child scroll is not smooth). May be some logical improvement require when scrolling bottom to top.

As i am new to Dart, could anyone help me to get this done ?

import 'dart:async';

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(title: 'Flutter Demo', home: MyListView());
 }
}

class MyListView extends StatelessWidget {
  ScrollController _mainScrollController = ScrollController();
  double listHeight = 370;
  @override
  Widget build(BuildContext context) {
return Scaffold(
  appBar: AppBar(
    title: const Text('AppBar'),
  ),
  body: Container(
    height: MediaQuery.of(context).size.height,
    child: ListView(
        controller: _mainScrollController,
        children: <Widget>[

          Container(height: listHeight,child: RapportList(parentScrollController: _mainScrollController)),
          OtherElement(text: "Other element 1 which will be scrolled",),
          OtherElement(text: "Other element 2 which will be scrolled",),
          OtherElement(text: "Other element 3 which will be scrolled",),
          OtherElement(text: "Other element 4 which will be scrolled",),
          OtherElement(text: "Other element 5 which will be scrolled",),
          Container(height: listHeight,child: RapportList(parentScrollController: _mainScrollController)),
           OtherElement(text: "Other element 4 which will be scrolled",),
          OtherElement(text: "Other element 5 which will be scrolled",),
          Container(height: listHeight,child: RapportList(parentScrollController: _mainScrollController)),


        ],
    ),
  ),
);
   }
  }

 class RapportList extends StatefulWidget {
    final ScrollController parentScrollController;
   RapportList({@required this.parentScrollController});
   @override
   _RapportListState createState() => _RapportListState();
     }

class _RapportListState extends State<RapportList> {
   ScrollPhysics physics = ScrollPhysics();
   // NeverScrollableScrollPhysics()
    ScrollController _listViewScrollController;


  void listViewScrollListener(){
     print("smth");
      if(_listViewScrollController.offset >= _listViewScrollController.position.maxScrollExtent &&
    !_listViewScrollController.position.outOfRange){
  if(widget.parentScrollController.offset==0){
    widget.parentScrollController.animateTo(50,duration: Duration(milliseconds: 200),curve: Curves.linear);
  }
  setState((){
    physics = NeverScrollableScrollPhysics();
  });
  print("bottom");
    }
   }

  void mainScrollListener(){
       if(widget.parentScrollController.offset <= widget.parentScrollController.position.minScrollExtent &&
    !widget.parentScrollController.position.outOfRange){
  setState((){
    if(physics is NeverScrollableScrollPhysics){
      physics = ScrollPhysics();
_listViewScrollController.animateTo(_listViewScrollController.position.maxScrollExtent-50,duration: Duration(milliseconds: 200),curve: Curves.linear);
    }
  });
  print("top");
   }
 }

 @override
 void setState(fn) {
   super.setState(fn);
 }
  @override
  void initState() {
    _listViewScrollController = ScrollController();
   _listViewScrollController.addListener(listViewScrollListener);

    // TODO: implement initState
    super.initState();
   }
   @override
   Widget build(BuildContext context) {

     widget.parentScrollController.addListener(mainScrollListener);
     return ListView.builder(
       controller: _listViewScrollController,
      physics: physics,
      shrinkWrap: true,
      itemCount: 50,
      itemBuilder: (context, index) {
        return ListTile(
         title: GestureDetector(
           child: Row(
             children: <Widget>[
               Container(child: Text("text $index")),
          ],
        ),
      ),
    );
  },
   );
  }
  }


 class OtherElement extends StatelessWidget {
  final String text;
  OtherElement({this.text});
   @override
    Widget build(BuildContext context) {
   return Container(
     height: 100,
     child: Center(child: Padding(
      padding: const EdgeInsets.symmetric(horizontal:40.0),
       child: Text(this.text,style:TextStyle(fontSize: 30)),
     )),
   );
   }
   }

The idea is taken completely from here : Flutter listview at the end of its content scrolls screen

Upvotes: 4

Views: 3832

Answers (1)

dengApro
dengApro

Reputation: 4038

you can try the following code,

  • 1, add child ListView 's scroll listening condition

if ((currentOffset >= maxBound || currentOffset <= minBound) && !_listViewScrollController.position.outOfRange)

  • 2 remove the unnecessary parent ListView auto scrolling code
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(title: 'Flutter Demo', home: MyListView());
      }
    }
    
    class MyListView extends StatelessWidget {
      ScrollController _mainScrollController = ScrollController();
    
      double listHeight = 370;
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('AppBar'),
          ),
          body: Container(
            height: MediaQuery.of(context).size.height,
            child: ListView(
              controller: _mainScrollController,
              children: <Widget>[
                Container(
                    height: listHeight, child: RapportList(_mainScrollController)),
                OtherElement(
                  "Other element 1 which will be scrolled",
                ),
                OtherElement(
                  "Other element 2 which will be scrolled",
                ),
                OtherElement(
                  "Other element 3 which will be scrolled",
                ),
                OtherElement(
                  "Other element 4 which will be scrolled",
                ),
                OtherElement(
                  "Other element 5 which will be scrolled",
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class RapportList extends StatefulWidget {
      final ScrollController parentScrollController;
      RapportList(this.parentScrollController);
      @override
      _RapportListState createState() => _RapportListState();
    }
    
    class _RapportListState extends State<RapportList> {
      late ScrollController _listViewScrollController = ScrollController()
        ..addListener(listViewScrollListener);
      ScrollPhysics _physics = ScrollPhysics();
      // NeverScrollableScrollPhysics()
    
      void listViewScrollListener() {
        double currentOffset = _listViewScrollController.offset;
        double maxBound = _listViewScrollController.position.maxScrollExtent;
        double minBound = _listViewScrollController.position.minScrollExtent;
        print("_listViewScrollController.offset : ${currentOffset} ___   ");
        print(" _listViewScrollController.position.maxScrollExtent : ${maxBound}");
        print(" _listViewScrollController.position.minScrollExtent : ${minBound}");
        print("/n/n/n");
        if ((currentOffset >= maxBound || currentOffset <= minBound) &&
            !_listViewScrollController.position.outOfRange) {
          setState(() {
            _physics = NeverScrollableScrollPhysics();
          });
          print("bottom");
        }
      }
    
      void mainScrollListener() {
        if (widget.parentScrollController.offset <=
                widget.parentScrollController.position.minScrollExtent &&
            !widget.parentScrollController.position.outOfRange) {
          setState(() {
            if (_physics is NeverScrollableScrollPhysics) {
              _physics = ScrollPhysics();
            }
          });
          print("top");
        }
      }
    
      @override
      Widget build(BuildContext context) {
        widget.parentScrollController.addListener(mainScrollListener);
        return ListView.builder(
          controller: _listViewScrollController,
          physics: _physics,
          shrinkWrap: true,
          itemCount: 50,
          itemBuilder: (context, index) {
            return ListTile(
              title: GestureDetector(
                child: Row(
                  children: <Widget>[
                    Container(child: Text("text $index")),
                  ],
                ),
              ),
            );
          },
        );
      }
    }
    
    class OtherElement extends StatelessWidget {
      final String text;
      OtherElement(this.text);
      @override
      Widget build(BuildContext context) {
        return Container(
          height: 100,
          child: Center(
              child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 40.0),
            child: Text(this.text, style: TextStyle(fontSize: 30)),
          )),
        );
      }
    }

Upvotes: 0

Related Questions