I would like to use hive in my Flutter app to save locally conversations and messages of the conversations for the current user.
That being said, i especially would like to retrieve all of the messages for a specific conversation.
Should I create boxes as the conversation_id as name to store messages ?
If i am correct, this would create 1 file per box, so is it correct to have possibly hundreds of files for this ? Does it sound reasonable ?
What would be the best way in this situation ?
So far i only have two boxes Messages and Conversations but as we can't "query", if i want to load messages for a specific conversation, i need to load ALL messages of ALL conversations and then filter in dart, which can eventually lead to Ouf Of Memory crash issue.
I have tested it with 100k messages to get the OOM, but i am seeing this on the long-run of course as a user will not have 100K messages before a very long time, but anyway it would improve performances.
Thanks in advance
I think u should add 2 more,
#HiveField(5)
late int conversationId;
#HiveField(6)
late bool isMe;
// 5 and 6 just distinct hive numbers
you should store conversationId and isMe variables on each messages with your existing varibales.
and when you try to display messages through this method,
Expanded(
child: Container(
color: Colors.grey.shade100,
child: ValueListenableBuilder<Box<ChatStore>>(
valueListenable: Boxes.getTransactions().listenable(),
builder: (context, box, _) {
final transactions = box.values
.toList()
.where((element) => element.conversationId == 1111)
.cast<ChatStore>();
return buildContent(transactions.toList());
},
),
)),
check conversation id with desired id example 1111 and isMe to change your right left alignment for two user.
//color: transaction.isMe == true ? Colors.white : Colors.green,
Widget buildContent(List<ChatStore> transactions) {
if (transactions.isEmpty) {
return const Center(
child: Text(
'No messages yet!',
style: TextStyle(fontSize: 24),
),
);
} else {
your logic to display messages
}
hope it helps
Related
I have a hive box opened in the main.dart and set it to a global variable so am able to access it from all other classes.
Now in one of my classes (settingsView.dart) which is a StatefulWidget am able to put data in the box in the form Map<String,Map<String,dynamic>>. To be specific the Map<String,dynamic> can be a Map<String,String> or Map<String,List>. e.g.
{"1A":{"num_on_roll": "34", "subjects": ["Mathematics","English","Science",...]}}
Now am also retrieving or reading this data and to display it in the UI the "num_on_roll" value in a Text widget and "subjects" value in a Wrap.
NOW THE PROBLEM.
The first ("num_on_roll") is always updated in the UI successfully but the "subjects" values in the Wrap are never updated unless I do hot restart or quit application and start it afresh, by so doing all data will be displayed successfully.
I have tried using ValueListenableBuilder to listen for changes in the box.
"class_constants" is the specific for the stored data which is Map<String,Map<String,dynamic>>.
ValueListenableBuilder(
valueListenable: Hive.box("mainDB").listenable(keys: ["class_constants"]),
builder: (context,Box box,child) {
var clsConst = box.get("class_constants", defaultValue: {});
return Wrap(
children: List.generate(
isPresent
? clsConst[classes[tab]]["subjects"].length
: selectedSubjects.length,
(index) => Text(
"${isPresent ? clsConst[classes[tab]]["subjects"][index] : selectedSubjects[index]}, ",
style: const TextStyle(
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic),
)),
);
}
),
Why is it that the data is store successfully but not displaying some part?
Please help me out.
I found the proper solution myself.
Hive has problem retrieving growable list immediately it has been put in the box. Even you must await it else it can't store!
So trick is to change growable list to non-growable list.
List list = [];
list.add(1);
list.add(2);
list.add(3);
// non-growable
List tempList = List.generate( list.length, (_)=> list[_], growable: false);
mainDB!.put('key',tempList)
;
I have a list of Expandable Items, each time I click in one of the items a stream is called returning a list of another items related to that I clicked on. The problem is if I quick expand two items of my initial list, the last remains with the items from the first one.
Example of what is expected:
(EXPANDABLE LIST)
Colombia
(items showed when I click on Colombia)
Bogotá
China
(items showed when I click on China)
Beijing
Example of what happens if I quickly open two items:
(EXPANDABLE LIST)
Colombia (clicked first, and before the load quickly click on China)
China
Bogotá
Is there a way to close or cancel or pause the stream every time I expand one item?
UPDATE
SCREEN
return ExpansionTile(
leading: items[index].image == null || items[index].image.isEmpty ? Image.asset(ASSET_NOIMAGE_URL,
fit: BoxFit.scaleDown
) : Image.network('${BASE_ROUTE_URL}/${ROUTE_SLASH}/${items[index].image}', fit: BoxFit.scaleDown,),
title: Text('${items[index].code} | ${items[index].desc}', style: TextStyle(fontWeight: FontWeight.bold, color: AppColorSecondary),),
children: [
Container(
height: MediaQuery.of(context).size.height * 0.75,
width: MediaQuery.of(context).size.width,
child: CountryDetails(items[index]),
),
],
WIDGET
class _CountryDetailsState extends State<CountryDetails> {
Country country;
#override
void initState() {
country = Provider.of<Country>(context, listen: false);
country.load(produtoGradeFVList: Provider.of<CountryListProvider>(context, listen: false).produtoGradeFVList).then((value) {
}); // here the stream is feed
super.initState();
}
You could set a debounce duration on the stream controller to avoid receiving two requests within a very short interval of time as:
_cityController.stream
.debounceTime(const Duration(milliseconds: 500))
.listen(_handlerFunctionForTapEvent);
Here, _cityController is your StreamController that's listening to the tap events of expandable items from the ui. Try setting a debounce time of 500 milliseconds, hopefully that would fix the issue.
Or you could also send the name of country along with the list of cities in the response from the stream and perform a check in the ui before displaying the city names in the expandable list.
I currently have a ListView that displays a large collection of objects. Quite a lot of these objects share the same first one/two words, for example:
Peter Johnson
Peter Jackson
Peter Smith
I would like to split this ListView into groups based on the start of their names. So for example all the Peters would be put in the same group. And then the next name that multiple people share would also be split into their own group.
The problem is that I can't explicitly say what these names are, how would I go about splitting these up?
This is a hard one, I'm going to try to simplify this as much as possible, but the general answer is first write an algorithm that splits your string how you want (in this case, if the first word is the same) and then nest these into a column or whatever.
Here I split the values into a map with the first name as a key and a list of all the people with that first name as a value
// Theres probably a better way to do this
Map<String, List<String>> valuesByFirstName(List<String> values) {
Map<String, List<String>> result = {};
for (final val in values) {
final fn = val.split().first;
if (result[fn] != null) result[fn]!.add(val);
else result[fn] = [val];
}
return result;
}
Then I'm not sure how you wanna group each of these so I just made some basic example:
var values = valuesByFirstName(originalValues);
return Row(
children: [
for (var key in values.keys)
SizedBox(
height: 200,
width: 100,
child: ListView(
children: [
Text(key, style: TextStyle(fontSize: 20)),
for (var value in values[key])
Text(value),
]
),
),
),
],
);
If what you want is to contract the different sections, take a look at the ExpansionPanel widget
I would like to know whether or not it is possible to utilise the print(); command in flutter to create a String in order to link said data to a Firestore database.
I am creating a customer satisfaction application in which the customer would press a button on the screen and said button would then omit a message to the database corresponding the reaction the customer selected.
Only issue is: I have not found a way to link onPressed:in way that it can omit such data to a server or locally.
Here is a brief bit of code to somewhat witness what I am trying to achieve:
Align(alignment: Alignment.center,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
onPrimary: Colors.cyanAccent,
primary: Colors.deepPurple,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(55.0),
),
),
onPressed: () async {
print('Good Chosen');
print(DateTime.now());
HapticFeedback.mediumImpact();
// Close the screen and return "Good Chosen" as the result.
Navigator.pop(context, 'Good Chosen');
},
child: Image.asset('assets/good pixel.png'),
),
),
Have you considered writing the Good Chosen string directly to firestore?
Instead of / additionally to your print statements, you could write
FirebaseFirestore.instance.collection("feedback").doc()
.set({'type': 'good_chosen', 'timestamp': DateTime.now(),}, SetOptions(merge: true));
I wanted to make my widget disappear after a few seconds, but it keep getting stuck on waiting.
if (unread != 0 && (getMessageObjects.length - count) == unread - 1) {
_groupedMessages.add(
FutureBuilder(
future: Future.delayed(Duration(milliseconds: 500)),
builder: (c, s) => s.connectionState == ConnectionState.done
? Container(
child: Chip(
label: Text('Finished'),
),
)
: Center(
child: Chip(
label: Text('${unread.toString()} unread messages'),
),
),
),
);
// reset
}
The _groupedMessages is a List. I have no idea if there are other ways, but please let me know if there are.
Your code looks odd but we need to see the rest of the code.
First thought, are you certain that the future isn't being constantly recreated and hence doesn't have a chance to complete?
It also seems odd that you are going to show a message indicating the count for 500ms then hide it.
Also why the artificial delay?
I'm guessing you need to fetch the count from a server in which case the future should contain the call to the server and complete when the results are returned.
I would the expect the UI to show a message 'fetching messages' and then change to 'unread messages x' once the fetch completes.
But these are just guesses given the limited amount of context you have provided.