value of my List is changing when i delete text from TextField Flutter - flutter

i have a list that i want to search in (I remove the values that don't match my search), I write the search word in a TextField and save the initial list in order to show it again when the user deletes the search word (or want to search for another thing) .
i can save the list in a variable but the issue is when I start deleting the search word the value of my initial list becomes the same that the list I show for the search !
onChanged: (val) {
//i save the list before doing any change here
if (_initList == null) _initList = myList;
// this part works fine
if (val.length > 2)
setState(() {
myList.removeWhere((element) => !element
.getName()
.toLowerCase()
.contains(val.toLowerCase()));
});
// when i’m in the else, when i try to print _initList value i find that it’s the same as myList
// the value of _initList is not changing anywhere else in the code
else {
setState(() {
myList = _initList;
});
}
},

when you do this
if (_initList == null) _initList = myList;
both variables will point to the same list. changes to one list will also change the other, because they are in fact the same list. What you want to do is safe a copy of the list in it. You can do it by calling toList() on it, like
if (_initList == null) _initList = myList.toList();

Related

I want to validate "Text" in flutter not (TextFormField or Textfield)

I want to validate "Text" in flutter not (TextFormField or Textfield), I want to check if the text is empty and if the text is empty then I want it to navigate to new page automatically.
So basically there's a Text widget where I am displaying data from Api using get method so what I want is, if the text widgets are empty, I want it to navigate to login page.
You need to store the string content of the concerned Text widget in a variable. Then you need to call a conditional operation on it to move to the new page.
Example:
String check = ""; (check == "") ? Navigator.pushNamed((context), "/desired-routeName") : Text(check),
simply you can check by == "";
for example:
String myText = ""
bool isEmpty(String text) => text == "";
isEmpty(myText); //returns true
//now set some value
myText = "some value"
isEmpty(myText); //returns false
then you can navigate based on condition like:
if (isEmpty(myText))
{ //navigation logic goes here
} else {
}
Method 1.
You can declare a variable and pass it to the Text widget. Put your condition on that variable.
Method 2.
Save your Text Widget into a variable and use the data: String? property to access the text inside Text widget.
final textWidget = const Text("Hello World");
if(textWidget.data?.isEmpty()) {
//...
}
else {
//...
}
You can put the condition in didChangeDipendencies() or initState() if you wanna to navigate to new page without any click event.
If you get data from api, i would recommend you to validate your string data with regular expressions. To check empty strings you can se the follow regex.
RegExp regExp = new RegExp(r"^$",caseSensitive: false, multiLine: false,);
if(regExp.allMatches(yourValue)){
Navigator.push(context,MaterialPageRoute(builder: (context) => const SecondRoute()),);
}

How to display only special parts in a list FLUTTER

I'm very new to flutter and I wondered how I can make something like that:
I want loop trough a List of Items and make a Widget for all of them. I do this with a for-block instead of a listView.builder, because so it isn't a list. But I want to display for example every Item which has "valid" as value for worth in a different block as them, which has "invalid" as value.
I thought I could do this like that:
Text("Valid"),
for (ValuePair item in items)
if (item.worth == "valid"){
return myOwnView(item);
},
Text("Invalid"),
for (ValuePair item in items)
if (item.worth == "invalid"){
return myOwnView(item);
};
What could I do instead of that?
First solution (preferable):
I would filter the values in the list first, than pass it to your own builder like that:
List<YourItemClass> yourUnsortedList = [...];
List<YourItemClass> yourSortedList = [...];
for (var I = 0; I < yourUnsortedList.length; i++) {
if (yourUnsortedList.worth == 'valid') {
yourSortedList.add(yourUnsortedList[i])
}
}
next - just build this list.
Second solution (bad one):
Inside your builder place if statement on top, and if you need nothing to be built, return empty container, like that:
List<YourItemClass> yourUnsortedList = [...];
Listview.builder(
itemCount: yourUnsortedList.length;
itemBuilder: (context, idx) {
if (yourUnsortedList[idx].worth == 'valid') {
return YourOwnItem(item);
} else {
return Container();
}
}
)
Try making two lists of type Widget, one for valid and the other for invalid.
So, we have:
List<Widget> validChildren = List.empty(growable=true);
List<Widget> invalidChildren = List.empty(growable=true);
then use for loop to add the children:
for(Valuepair item in items)
if(item.worth == "valid")
validChildren.add(MyOwnView(item));
else
invalidChildren.add(MyOwnView(item));
you can add these lists as children of a column or a row.
You can give continue; in place of invalidChildren.add() if you don't want that list.

Showing multiple search results on leaflet map [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
https://www.website.ro/harta this is the map that uses leaflet.js and https://github.com/stefanocudini/leaflet-search, but i can search only single locations.
https://www.website.ro/public/ajax?q=electri
If i search for "electri" it has 3 locations, i want to show them when i hit enter, not to show "Not found".
Already searched on google, stackoverflow, didnt found similar answer/problem.
This can be done with careful use of the options that leaflet-search provides. First, let's create an array that will hold the potential results, and a featureLayer to render any results that show up:
const results = [];
var resultsLayer = L.featureGroup();
Now we can overwrite the buildTip option as a function which does pretty much what it does already by default, but pushes the results to an array as well:
var controlSearch = new L.Control.Search({
...options,
// hijack buildtip function, push results to array
buildTip: (text, loc) => {
results.push(loc); // <---- crucial line here
// the rest of this is lifted from the source code almost exactly
// so as to keep the same behavior when clicking on an option
const tip = L.DomUtil.create("div");
tip.innerHTML = text;
L.DomEvent.disableClickPropagation(tip)
.on(tip, "click", L.DomEvent.stop, controlSearch)
.on(
tip,
"click",
function (e) {
controlSearch._input.value = text;
controlSearch._handleAutoresize();
controlSearch._input.focus();
controlSearch._hideTooltip();
controlSearch._handleSubmit();
},
controlSearch
);
return tip;
},
// only move to the location if there are not multiple results
moveToLocation: results.length
? () => {}
: L.Control.Search._defaultMoveToLocation
});
Now we add an event listener to the input of the search, and if the user presses enter, and there are multiple results, the results that were pushed into the results array will be added to the resultsLayer as markers, and added to the map:
inputEl.addEventListener("keypress", function (e) {
if (e.key === "Enter" && results.length) {
markersLayer.remove();
results.forEach((result) => {
const marker = L.marker(result);
resultsLayer.addLayer(marker);
});
map.fitBounds(resultsLayer.getBounds());
}
});
Working codesandbox
Note this will likely require some cleanup work (i.e. emptying the array on new or empty searches), or readding the full data set if the search is empty, etc., but this should be enough to get you started.
Edit - Full item info
You asked in a comment how we can get the full details of an item and put that in a popup. Reading through leaflet-search's docs and source code, there doesn't seem to be any place that their code 'catches' the entire data object. The buildTip function really only needs 2 pieces of data from an item - the text to show in the tooltip, and the location it refers to. There's a bunch of TODOs regarding keeping the source data in a cache, but they're still todos.
What I would do is use the title and loc that is returned in a result to filter the original data and find its corresponding item in the original data:
const getFullItem = (title, loc) => {
return data.find((item) => item.title === title && loc.equals(item.loc));
};
We can also create a generic function to build the popup text for all the makers, and the results, so the popups are all consistent:
const buildPopupText = (item) => {
return `
<h4>Title: ${item.title}</h4>
<p>Phone: ${item.telefon}</p>
<p>more stuff from ${item.whatever}</p>
`;
};
When we hit enter and we map through the results, we'll use the result to get the original item:
inputEl.addEventListener("keypress", function (e) {
if (e.key === "Enter" && results.length) {
results.forEach((result) => {
const originalItem = getFullItem(result.text, result.loc);
const marker = L.marker(result.loc);
marker.bindPopup(buildPopupText(originalItem));
resultsLayer.addLayer(marker);
});
map.fitBounds(resultsLayer.getBounds());
}
});
So now the results popups build a popup from the originalItem, which has all the properties you'll need.
Working codesandbox

Adding and Removing Items From Flutter Lists Dynamically

I have these two functions that should add the item onTap to the list, and then another one to delete the item. The first one (adding the item) works fine, but the delete one doesn't seem to delete anything. I suspect the issue is that the counter variable is not global, but for some reason it doesn't work fine when I add it globally (and I would need it to be). Here's the full code:
List<int> _totalPrice = [];
List<int> get totalPrice => _totalPrice;
here is the add item function
Future getTotal(item) async {
int counter = 0;
_totalPrice.add(int.parse(item));
_totalPrice.forEach((element) => counter += element);
print('LIST: $_totalPrice');
print('SUM: $counter');
return counter;
}
here is the delete function that doesn't remove anything
deleteSumItem(String item) {
_totalPrice.remove(item);
}
I think the issue is that the counter variable isn't global, I am not sure how to add it globally to change dynamically.
So, here your code shows that you are putting int in your _totalPrice list, but you are trying to remove the String from the list. It will not be found to be deleted.
So, you can change the data type of your list into String or you can write the below function to delete an item where the function only takes an int
deleteSumItem(int item) {
_totalPrice.remove(item);
}
Also you can remove an item by below line of code (If you have a custom type of data in your list):
_totalPrice.removeWhere((item) => item.price == '520')

Flutter - Replace a subsstring with a Widget

I am building an application with flutter, but I am having a bit of trouble with one of my widgets. I am getting a JSON response from an API endpoint in order to build the comments on posts, but I need to be able to take part of a string and wrap it in a GestureDetector, in order to handle "# mentions".
For example: I have the string hey there #MattChris how are you? I need to be able to wrap the #MattChris in a GestureDetector.
At the moment I parse the incoming string and provide a list with each space-separated word from the actual comment. Like so:
List<Widget> comment = new List();
outer: for (String word in json['content'].toString().split(" ")) {
if (word != null && word.isNotEmpty) {
if (word.startsWith('#')) {
comment.add(GestureDetector(
onTap: goToProfile,
child: Text(word + ' ')
);
} else {
comment.add(Text(word + ' '));
}
}
}
Only issue now is that's a lot of extra elements taking up memory, and a difficulty with ensuring that the text wraps in the way I expect. I've seen the answer here, but I'm not sure how to ensure that text wraps as if it were one string in a Text widget.
I was able to come to a working solution. Reading from the implementation I liked to again, and looking in the comments, I decided to use a recursive function:
List<TextSpan> _mentionParser(String message, Iterable<dynamic> mentions) {
if (message == null || message.isEmpty) // Don't return anything if there is no message.
return [];
for (Map<String, dynamic> mention in mentions) { // Loop through the list of names to replace
if (message.contains("#${mention['username']}")) { // If the message contains the name to replace
List<TextSpan> _children = [];
String preUsernameMessage = message.substring(0, message.indexOf("#${mention['username']}")).trimLeft(); // Get everything before the mention
if (preUsernameMessage != null && preUsernameMessage.isNotEmpty)
_children.add(TextSpan(children: _mentionParser(preUsernameMessage, mentions))); // if it isn't empty, recurse and add to the list
_children.add( // Always add the display name to the list
TextSpan(
text: "${mention['display_name']}".trim(),
style: TextStyle(color: Color(0xff2e6da4)),
recognizer: TapGestureRecognizer()
..onTap = () => {gotoProfile(json['username'])}
)
);
String postUsernameMessage = message.substring(message.indexOf("#${mention['username']}") + "#${mention['username']}".length, message.length).trimRight(); // Get everything after the mention
if (postUsernameMessage != null && postUsernameMessage.isNotEmpty) // If it isn't empty, recurse and add it to the list
_children.add(TextSpan(children: _mentionParser(postUsernameMessage, mentions)));
return _children; // return the constructed list
}
}
return [TextSpan(text: message)]; // If the string didn't contain any of the strings to replace, then just return the message as passed.
}
Then I just call this as the children variable on a TextSpan inside Text.rich. It took some time, but I was able to get the implementation working!
public String method(String str) {
if (str != null && str.length() > 0 && str.charAt(str.length() - 1) == 'x') {
str = str.substring(0, str.length() - 1);
}
return str; // to remove last character
}