How to 'cancel' the stream data response after request another one - flutter

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.

Related

Generic filter Flutter

Goodmorning,
I'm developing an app with flutter but I'm facing some problems with Provider (I think something miss in my knowledge).
My app fetch data from my API and displays them in listview.
In whole app I have different screen which displays different data type in listview and now I want to create filtering logic.
To avoid rewrite same code multiple times I thought to create one screen to reuse for filtering purposes but I'm facing problems with state management.
What I did:
create base model for filter information
`
enum FilterWidget { TEXT_FIELD, DROPDOWN } //to resolve necessary Widget with getWidget() (to implement)
class FilterBaseModel with ChangeNotifier {
String? value= 'Hello';
FilterWidget? widgetType;
FilterBaseModel(this.value, this.widgetType);
onChange() {
value= value== 'Hello' ? 'HelloChange' : 'Hello';
notifyListeners();
}
}
`
One screen for display filters depending on request
List<FilterBaseModel> filters = [];
FilterScreen() {
//Provided from caller. Now here for test purposes
filters.add(FilterBaseModel('Filter1', FilterWidget.TEXT_FIELD));
filters.add(FilterBaseModel('Filter2', FilterWidget.TEXT_FIELD));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SafeArea(
minimum: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
child: SingleChildScrollView(
child: Container(
height: 400,
child: Column(
children: filters
.map(
(e) => Consumer<FilterBaseModel>(
builder: (_, filter, child) =>
ChangeNotifierProvider.value(
value: filter,
child: CustomTextField(
`your text` initialText: e.value,
onTap: () {
e.onChange();
filter.onChange();
},
),
),
),
)
.toList(),
))),
),
);
}
`
The problem is in Consumer and ChangeNotifier.value.
Screen works quite well: widget are displayed and callback are called, what is wrong? I need to use onChange method of both instance to update UI otherwhise method was called but widget is not rebuilt.
I know that probably putting consumer there is not right way but I tried also to put outside but doesn't work.
I expect to have one filter screen which receives in input filters list information, display them, handle their state managment and return their value.
P.S: this code now works, but I know is not the right way
Thank you for help!
EDIT
Have same behaviour without ChangeNotifierProvider.value. Therefore I'm more confused than before because still persist the double call to onChange for correct rebuilding.
More bit confused about ChangeNotifierProvider.value using...

How do i modify the data of an existing variable in flutter?

I want to make an editable TextWidget in flutter but I don't really know how to go around it, I did some research, but still can't find a good solution.
Here's my sample code below.
I have a variable called
int qty = 1;
and so I called the variable in TextWidget
Column(
children: [
Text(
"${qty}",
style: TextStyle(),
)
],
),
I want to have these features that make user tab on the value to change it if they want, upon tap, a pop-up dialog will show to give the user the ability to change the existing value to whatever the user wants.
Please if anyone knows how, please help.
You will need a statfull widget to call setState and make the UI update with the new value stored in your qty variable. (I'am assuming that you are not using any state managment).
I wrote a possible solution for what you need.
Let look into some considerations:
Text will show whatever is in the qty as long we call setState after (or do it inside) we change the value of qty.
You need some widget to detect your tap. If you want to the text be 'clicable' then it should be wraped inside that widget.
The onTap/onPress call back of that widget should show a new widget. For this you can use the already made showDialog() and pass it a Dialog Widget. in here you will put your ui for that.
In some point of that UI you need to introduce the new value. So you can use a simple TextField that will save the introduced value, where you can assign it to qty, without forgetting to call setState! Note that it deal with strings, so you neet to do an int.parse() ou double.parse accordingly to you qty var type.
And I think that's it.
The could be other ways of doing it. This is a good and simple approach for your need.
I wrote a piece of code to help or somelse how is trying to do it:
InkWell(
// can be gesture detector, button, etc
onTap: () => showDialog(
context: context,
builder: (context) => Dialog(
child: Container(
color:
Colors.white60, // change it accordingly to you
height: 80, // change it accordingly to you
width: 200, // change it accordingly to you
child: Column(
children: [
const Text('Change your value here'),
TextField(
decoration:
InputDecoration(hintText: qty.toString()),
onChanged: (insertValue) => setState(() {
qty = int.parse(insertValue);
}),
// you can use other callBack function (like onComplete,
// onSaved), wich is more eficient than calling setState eveytime,
// but you have to do the needed adtaptions. Like onSave
// needs a key to call the save function. is easy just google it.
),
],
)),
)),
child: Text(
"${qty}",
),
),
What you are probably looking is a DropdownButton.
You would have something like this:
int qty = 1;
List<int> listOfValues = [1,2,3,4];
and then in your column you would have
DropdownButton<int>(
// This are the list of items that will appear in your dropdown menu.
// items is all the options you want your users to be able to select from,
// and it take a list of `DropdownMenuItem`. So instead of creating a `DropdownMenuItem`
// for each of the items in `listOfValues`, we iterate through it and return
// a `DropdownMenuItem`
items: listOfValues
.map((item) => DropdownMenuItem<int>(
value: item,
child: Text('$item'),
))
.toList(),
value: qty,
onChanged: (value) {
if (value != null) {
setState(() {
qty = value;
});
}
},
),
For more information on DropDownButton, check the following links:
https://api.flutter.dev/flutter/material/DropdownButton-class.html
https://www.youtube.com/watch?v=K8Y7sWZ7Q3s
Note: In a scenario where you want to increase the quantity of an item, like in a shopping cart, maybe having a button increment qty by 1 would be better.

Hive conversations / messages architecture in Flutter

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

How to create recommended ListView in flutter and fire store

There's a way to create a recommended from user ListView using fire base and flutter...
For example I have a list of data in firebase that I am fetching them from firebase as I show them in the data list screen, and I have a list of recommended list view from user for example the clicked data item from user something shows like the below image:
To be more specific how figure if the data was viewed by user or not?
There's a way or docs to do something like this?
In case your intention is to provide some kind of "user likes" functionality.
You can create Provider of ChangeNotifier with Provider package at root (for example) and store Set<YourShopItem> there.
Then expose methods like add(YourShopItem item) and remove(YourShopItem item) on this ChangeNotifier which should add and remove items from your set and call notifyListeners() every time you call this method.
Then when you need to determine if your item is liked just obtain your ChangeNotifier and check if item is in set. Your widgets is gonna be updated every time add or remove methods are called because of their subscription to ChangeNotifier through Provider.
In case your intention is to track visibility of item.
You can use visibility detector package to track whether certain widget is visible. You can subscribe to certain widget and when it's shown, a callback is gonna be fired. So you should wrap every widget of your list into VisibilityDetector widget and save that your item was viewed.
Something like that should do the job:
final List<String> entries = <String>['A', 'B', 'C'];
final List<int> colorCodes = <int>[600, 500, 100];
ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: entries.length,
itemBuilder: (BuildContext context, int index) {
return VisibilityDetector(
onVisibilityChanged: (VisibilityInfo info) {
if (info.visibleFraction == 1) {
ON_YOUR_ITEM_IS_VISIBLE_FUNCTION();
}
},
child: Container(
height: 50,
color: Colors.amber[colorCodes[index]],
child: Center(child: Text('Entry ${entries[index]}')),
),
);
}
);
Also refer to this: https://stackoverflow.com/a/63577928/13737975.

Flutter: How to make a widget disappear after a few seconds?

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.