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
How do I make my ListView builder not scrollable? You can make the ListView widget never scrollable by setting physics property to NeverScrollableScrollPhysics().
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.
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.
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.
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()));
}
},
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With