Flutter how to create List view autoscrolling?
Asked Answered
G

4

9

I'm trying to make a ListView that auto-scrolls to the newest data point. I tired to do this by making a _scrollToBottom function that uses the .jumpTo method.

But i get a blank screen in the app, and 'child.parentData != null': is not true. in the debug console.

Any suggestions on how i can implement auto-scrolling?

Here is the relevant portions of my current code:

ScrollController _scrollController = ScrollController();

_scrollToBottom(){  _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}

@override
  Widget build(BuildContext context) {
    return StreamBuilder(
      stream: DataShareWidget.of(context).stream,
      builder: (BuildContext context, AsyncSnapshot snapshot){
        if(snapshot.hasError){ return Text(snapshot.error);}
        if(snapshot.hasData){
          _dataFormat(snapshot.data);
          return ListView.builder(
            itemCount: _listViewData.length,
            controller: _scrollController,
            reverse: true,
            shrinkWrap: true,
            itemBuilder: (context, index) {
              _scrollToBottom();
              return ListTile(
                title: AutoSizeText(_listViewData[index], maxLines: 2),
                dense: true,
              );
            },
          );
        }
      }
    );
  }
Guitarfish answered 20/6, 2019 at 12:12 Comment(1)
Please try title: AutoSizeText(_listViewData[index] == null ? 'Loading..' : _listViewData[index] , maxLines: 2) – Mazdaism
H
15

What you need is to call _scrollToBottom() method once the list is built fully.

Modification is your code (without StreamBuilder):

      ScrollController _scrollController = ScrollController();
    
      _scrollToBottom() {
        _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
      }
      
      @override
      void initState() {
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToBottom()); 
        return Scaffold(
          body: ListView.builder(
            itemCount: 50,
           // itemCount: _listViewData.length,
            controller: _scrollController,
            reverse: true,
            shrinkWrap: true,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text('Yo Dummy Text $index'),
                // title: AutoSizeText(_listViewData[index], maxLines: 2),
                dense: true,
              );
            },
          ),
        );
      }
Heilner answered 20/6, 2019 at 12:46 Comment(1)
I made an edit, ill mark this as the correct answer after you've approved it. PostFrameCallback needs to be called every time it builds. To get it to work i just moved it from initState function to the build function – Guitarfish
M
2

You need to do this and work perfectly....

  ScrollController _scrollController = ScrollController();
  
  @override
  Widget build(BuildContext context) {
    _scrollController.animateTo(_scrollController.position.maxScrollExtent, duration: Duration(milliseconds: 200), curve: Curves.easeOut);
    return StreamBuilder(
      stream: stream = Firestore.instance
          .collection('your collaction')
          .document('your document')
          .snapshots(),
      builder: (context, snapshot) {
        return snapshot.hasData
            ? ListView.builder(
                controller: _scrollController,
                shrinkWrap: true,
                itemCount: snapshot.data.documents.length,
                itemBuilder: (context, index) =>
                    msgTile(snapshot.data.documents[index], user1),
              )
            : Text('Loading...');
      },
    );
  }

 
Manvil answered 3/7, 2020 at 14:52 Comment(0)
S
2

In flutter 3.7+ it can be easily done using the following code.


class _HomePageState extends State<HomePage> {
  ScrollController scrollController = ScrollController(); // πŸ‘ˆ Define scrollController 
  List<String> assets = [...] // String of images to be displayed in listview

  @override
  void initState() { // πŸ‘ˆ create animation in initState
    Future.delayed(const Duration(seconds: 1), () {
      scrollController.animateTo(scrollController.position.maxScrollExtent,
          duration: Duration(seconds: asset.length * 10), curve: Curves.linear);
    });

   //πŸ‘‰ If you want infinite scrolling use the following code 
    scrollController.addListener(() {
      if (scrollController.position.pixels ==
          scrollController.position.maxScrollExtent) {
        // Scroll has reached the end, reset the position to the beginning.
        scrollController.jumpTo(scrollController.position.minScrollExtent);
      }
    });
    super.initState();
  }

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

@override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;
    return Scaffold(
      body: SizedBox(
        width: size.width,
        height: size.height,
        child: ListView.builder(
                  controller: scrollController, // πŸ‘ˆ assign scrollController here
                  ....
                  // display your images here
               ),
             ),
       );
  }
Schmitz answered 25/4, 2023 at 4:35 Comment(1)
how can I continuously scroll for same list repetitively[without passing length*2 or 3 haha] and not reset it's position to beginning? – Yingyingkow
L
0

The problem is in your StreamBuilder code. If the snapshot isn't ready you need to return something. Try this code:

ScrollController _scrollController = ScrollController();

_scrollToBottom(){  _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}

@override
  Widget build(BuildContext context) {
    return StreamBuilder(
      stream: DataShareWidget.of(context).stream,
      builder: (BuildContext context, AsyncSnapshot snapshot){
        if(snapshot.hasError){ return Text(snapshot.error);}
        if(snapshot.hasData){
          _dataFormat(snapshot.data);
          return ListView.builder(
            itemCount: _listViewData.length,
            controller: _scrollController,
            reverse: true,
            shrinkWrap: true,
            itemBuilder: (context, index) {
              _scrollToBottom();
              return ListTile(
                title: AutoSizeText(_listViewData[index], maxLines: 2),
                dense: true,
              );
            },
          );
        }
        //Something waiting the snapshot
        return CircularProgressIndicator();
      }
    );
  }
Legacy answered 20/6, 2019 at 12:37 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.