I've got a SQLite database that I query data from. From such data I create the necessary widgets and store them in a list. Passing the list to ListView.builder I create the items from the list. Everything looks good except when I add new data to the widget list. Nothing shows up if I use insert
. If I use add
there's no issue. Here's the snippet.
List<MessageItem> _messageItems = <MessageItem>[];
// Reads the data and creates the widgets (all OK here)
// Called when reading the data <<<
_readDBMessages() async {
List<Map> messages = await readMessages(_threadID);
List<MessageItem> holderList = <MessageItem>[];
for (final msg in messages) {
holderList.add(new MessageItem(
msg['message'],
msg['timestamp'],
msg['status'],
_contactData['photo']));
}
_messageItems.clear();
setState(() {
_messages = msges;
_messageItems = holderList;
});
}
// When I use _messageItems.add(new MessageItem()); the item shows as expected but it
// it's located on top of the list. Since my ListView is reverse
// I instead use _messagesInsert(0, new MessageItem()); in doing so
// The list is not updated. Scrolling up/down will than proceed to
// show the item as expected.
// Called when storing the data <<<
_storeNewMessage(String message) {
int timestamp = new DateTime.now().millisecondsSinceEpoch;
storeMessage(_threadID, message, _me, 'sending', timestamp, 'text').then((onValue) {
// _readDBMessages();
setState(() {
_messageItems.add(
new MessageItem(
message, timestamp, 'sending', null
)
);
print('Message inserted ---------------');
});
});
}
// Here's my listView constructor <<<
new Expanded(
child: new ListView.builder(
reverse: true,
padding: const EdgeInsets.all(12.0),
itemCount: (_messageItems == null) ? 0 : _messageItems.length,
itemBuilder: (context, i) => _messageItems[i]
)
),
According to Flutter doctor everything is OK, Thanks for the help ;-)
The main difference between ListView and ListView. builder is that ListView creates all items at once, whereas the ListView. builder() constructor creates items when they are scrolled onto the screen.
It constrains its box children to have a specific given extent along the main axis. The prototypeItem property, which allows forcing the children's extent to be the same as the given widget.
Perhaps you should pass a unique key to your MessageItem
.
When you receive a data:
new MessageItem(
msg['message'],
msg['timestamp'],
msg['status'],
_contactData['photo'],
key: Key(msg['message']+msg['timestamp'].toString())
)
When you add a new entry:
_messageItems.add(
new MessageItem(
message, timestamp, 'sending', null, key: Key("${message}${timestamp}")
)
);
In MessageItem
constructor:
MessageItem(..., {Key key}): super(key: key);
Another way is to specify a random key for the list, something like this:
child: new ListView.builder(
key: new Key(randomString()),
You can read about the keys in https://flutter.io/widgets-intro/#keys or check Dismissible
widget as an example
I had a similar problem where I was listening to Floor db changes with StreamBuilder, in which the StreamBuilder was reacting but ListView was not getting updated. Tried the above solution
child: new ListView.builder(
key: UniqueKey(),
The ListView was then getting updated with new data but there was one problem, in the above implementation the ListView gets refreshed which works great with new data additions, but when I update a data in db or remove a data using dismissible widget, the ListView is again refreshed and the first element is displayed.
So I tried adding the key to each widget in the itemBuilder and it worked for me
ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: array.length,
itemBuilder: (context, index) {
return ListItemWidget(
dao: dao,
item: array[index],
key: UniqueKey(),
);
},
);
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