Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep scroll position in ListView when adding new item on top

Tags:

flutter

I am using a ListView.builder to render a chat.

The scenario is the following: A user types in a new chat message, when that happens i am storing the new message in state and therefore the itemCount is increased by one.

If i am at the end of the list, I see the new item directly at the top, everything is OK.

However, if I am in the middle of the chat and an item is added, the view scrolls a little bit. How can I disable this behaviour? I guess it kinda makes sense because the scroll offset is still the same double in the ScrollControllers state, but if there is one more item, it looks like it scrolls...

Is there a good way to do this? I guess the manual approach would be to measure the height of the new item and manually set the correct ScrollController offset again.. but meeeh

like image 675
wwwdata Avatar asked Aug 14 '18 15:08

wwwdata


People also ask

How do I make my ListView builder not scrollable?

How do I make my ListView builder not scrollable? You can make the ListView widget never scrollable by setting physics property to NeverScrollableScrollPhysics().

What is the difference between SingleChildScrollView ListView and ListView builder?

By using ListView, only the items that are visible are mounted and painted. On the other hand, by using SingleChildScrollView+Column, the entire item list is mounted+painted, even if only a few items are visible. The tradeoff is that ListView is less flexible.

How do I scroll to a specific position in ListView in flutter?

All you have to do is set Global Keys for your widgets and call Scrollable. ensureVisible on the key of your widget you want to scroll to. For this to work your ListView should be a finite List of objects.


2 Answers

In fact, you should not add new chat items to the list. And you have to cache them and then inject the cache into the list after the list has reached the top or bottom. I hope this solution works for you.

like image 88
MBN Avatar answered Oct 02 '22 19:10

MBN


This is a solution for items with static/constant height (height: 100) added on top of the list.

When I detect that list length has changed I change offset position by hardcoded item height by calling _scrollController.jumpTo(newOffect).

It works well. The list stays at the current position when a new item is added to the top, with no jumps visible.

class FeedWidget extends StatefulWidget {
  FeedWidget({
    Key key,
  }) : super(key: key);

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

class _FeedWidgetState extends State<FeedWidget> {
  var _scrollController = ScrollController();
  var _itemsLength = 0;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<List<FeedItem>>(
      stream: Provider.of<FeedRepository>(context).getAll(),
      builder: (BuildContext context, AsyncSnapshot<List<FeedItem>> items) {
        print('Loaded feed data $items');
        switch (items.connectionState) {
          case ConnectionState.waiting:
            return Center(child: CircularProgressIndicator());
          default:
            final newItemsLength = items.data.length;
            if (_itemsLength != 0 && _itemsLength != newItemsLength) {
              final newItemsCount = newItemsLength - _itemsLength;
              print('New items detected: $newItemsCount');
              final newOffset = _scrollController.offset +
                  100 * newItemsCount; //item height is 100
              print(
                  'Setting new offest ${_scrollController.offset} -> $newOffset');
              _scrollController.jumpTo(newOffset);
            }
            print('Items length new = $newItemsLength old = $_itemsLength}');
            _itemsLength = newItemsLength;
            return Flexible(
                child: ListView(
                    key: PageStorageKey('feed'), //keeps scroll position
                    controller: _scrollController,
                    padding: EdgeInsets.only(top: 8, bottom: 32),
                    children: items.data
                        .map((element) => FeedItemCard(item: element)) //item height is 100
                        .toList()));
        }
      },
    );
  }
}

like image 21
Dariusz Bacinski Avatar answered Oct 02 '22 21:10

Dariusz Bacinski