Multiple Select in DropdownButton in flutter - flutter

I know that there is many ways / package to implement multi select in dropdownbutton in flutter like --> this one
But with my little knowledge, I want to reinvent the wheel for basic building!!!
My scenario ->
I have a list of location's in json format like this -->
[{id: 928, location: Amtoli}, {id: 905, location: Ashok Tala}, {id: 899, location: Badur Tola}]
and two List -->
List _location = new List(); // this comes from API;
List _multiSelectLoc = new List();
And in DropDownButton's onChanged property -->
onChanged: (newValue) {
setState(() {
_location.forEach((e) {
if (e["id"] == newValue.toString()) {
_multiSelectLoc.add(e);
print(_multiSelectLoc);
}
});
_location.removeWhere(
(e) => e['id'] == newValue.toString());
print(_location);
});
},
I am curious to know why my code was not working, why I can't remove data from List _location, and and add to List _multiSelectLoc
I already simulated such condition in dartpad and it's just woking fine!

I guess I found the problem,
rewritten code -->
onChanged: (newValue) {
_location.forEach((e) {
if (e['id'].toString() == newValue.toString()) {
_multiSelectLoc.add(e);
// print(_multiSelectLoc);
}
});
_location.removeWhere(
(e) => e['id'].toString() == newValue.toString());
setState(() {
// print(_location);
});
},
I think the problem was, I was using a dynamic type to compare with string type, when I added .toString() the code started to giving me the results!

Related

How to keep the current option in group button flutter

I was used group_button package from flutter to make user can select many options. After upgrading the library to version 5.0.0 or above, the selectedButtons property has been deprecated. I was save options of user by it but no I can't use it now. I was use it as:
GroupButton(
selectedButtons: setSelectedBtn(index),
isRadio: false,
spacing: 10,
onSelected: (i, isSelected) =>
onFilterSelect(index, i, isSelected),
buttons: widget.filters[index].value
.map((e) => e.value)
.toList())),
setSelectedBtn(index) {
List<int> result = [];
var valueSet = widget.filters[index].value;
bool isSelected = valueSet
.firstWhere((element) => element.isSelected,
orElse: () => CategoryFilterValues("", false))
.isSelected;
if (!isSelected) {
return result;
}
for (int index = 0; index < valueSet.length; index++) {
if (valueSet.elementAt(index).isSelected) {
result.add(index);
}
}
return result;
}
group_button
How I can save the options now in new version? Anyone can help me to do it.
thank you
Use GroupButtonController.
Controller have list of methods to interact with options.
selectIndex(int i) - Select button by index in radio type
unselectIndex(int i) - Unselect button by index in checkbox and radio type
... etc.
More docs and examples you can find here

React Testing Library render leaking between tests

I'm currently writing some tests and noticed that the rendered screen is being carried over between tests.
The following 3 tests are being affected:
describe('rolls', () => {
it('are ordered', async () => {
render(
<Provider store={store}>
<RollGroup group={0} abilities={mockAbilities} />
</Provider>
);
await clickAllRollButtons();
for (const { rolls } of store.getState().rollGroups[0]) {
for (let i = 0; i < (rolls?.length as number) - 1; ++i) {
expect((rolls as number[])[i]).toBeGreaterThanOrEqual(
(rolls as number[])[i + 1]
);
}
}
});
it('are between 1 and 6', async () => {
render(
<Provider store={store}>
<RollGroup group={0} abilities={mockAbilities} />
</Provider>
);
await clickAllRollButtons();
for (const roll of store
.getState()
.rollGroups[0].reduce(
(prev: number[], { rolls }) => prev.concat(rolls as number[]),
[] as number[]
)) {
expect(roll).toBeGreaterThanOrEqual(1);
expect(roll).toBeLessThanOrEqual(6);
}
});
});
it('removes selected options from options of other dropdowns', async () => {
render(
<Provider store={store}>
<RollGroup group={0} abilities={mockAbilities} />
</Provider>
);
await clickAllRollButtons();
const dropdowns = screen.getAllByLabelText(/Select ability/i);
const selected = [];
await userEvent.selectOptions(dropdowns.at(0) as HTMLElement, 'CON');
selected.push('CON');
const secondDropdown = dropdowns.at(1) as HTMLSelectElement;
for (const s of selected) {
expect(getSelectOptions(secondDropdown)).not.toContain(s);
}
await userEvent.selectOptions(secondDropdown, 'STR');
selected.push('STR');
const thirdDropdown = dropdowns.at(2) as HTMLSelectElement;
for (const s of selected) {
expect(getSelectOptions(thirdDropdown)).not.toContain(s);
}
await userEvent.selectOptions(thirdDropdown, 'DEX');
selected.push('DEX');
const fourthDropdown = dropdowns.at(3) as HTMLSelectElement;
for (const s of selected) {
expect(getSelectOptions(fourthDropdown)).not.toContain(s);
}
await userEvent.selectOptions(fourthDropdown, 'INT');
selected.push('INT');
const fifthDropDown = dropdowns.at(4) as HTMLSelectElement;
for (const s of selected) {
expect(getSelectOptions(fifthDropDown)).not.toContain(s);
}
await userEvent.selectOptions(fifthDropDown, 'CHA');
selected.push('CHA');
const sixthDropdown = dropdowns.at(5) as HTMLSelectElement;
for (const s of selected) {
expect(getSelectOptions(sixthDropdown)).not.toContain(s);
}
});
Also relevant to the issue is the clickAllRollButtons function:
const clickAllRollButtons = async () => {
for (const button of screen.getAllByText(/Roll/i)) {
await userEvent.click(button);
}
};
When I try running the entire suite of tests, the tests fail with the following error:
TestingLibraryElementError: Unable to find an element with the text: /Roll/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
I looked at the markup returned in the error message and it's the markup I expect to see after all the roll buttons are clicked and there are none left. However, when I run only one of the tests using it.only, I get a success.
At first, I thought it might be an issue with my redux store carrying state between tests. I tried replacing the use of store with a mock store that gets set before each tests like so:
beforeEach(() => {
mockStore = {...store};
});
This did not work. With that in mind, the only reason I can see this happening is that react testing library is keeping the same screen state between tests, possibly because they all use the same jsx. If I'm correct, is there a way to clear the screen between each test? If not, what can I do?
Update: I made a codesandbox to reproduce the issue.
It turns out the issue was with my redux store. Reusing the same store between tests caused the state to leak into other tests. I solved this by exporting a function getStore and calling that whenever I need the store instead of exporting store.

VSCode custom language extention - Show CompletionItems for variable

I'm working on a custom language extension and I'm having issues with how to show functions for a variable.
I'm importing my language in the form of json, so i've created a typescript-file that i import into my extensions.ts:
export interface CustomIntellisense {
text: string;
help: string;
}
const data = [{
"text": "Void.String",
"help": "<h1>String String()</h1><p>Default constructor.<code>String something;</code></p>"
},
{
"text": "String.toInteger",
"help": "<h1>Integer toInteger()</h1><p>Converts a String to its numeric representation."
},
{
"text": "Void.Integer",
"help": "<h1>Integer Integer()</h1><p>Default constructor.</p>"
}];
export let json: CustomIntellisense[] = data;
My idea here is that the elements containing "Void" in text gets created as a variable, while the other element gets added as a method.
const provider1 = vscode.languages.registerCompletionItemProvider({ language: 'myLanguage', scheme: 'file' }, {
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
let items: vscode.CompletionItem[] = [];
let re = /\"/gi;
json.forEach(element => {
const item = new vscode.CompletionItem(element.text.split('.')[1]);
item.insertText = new vscode.SnippetString(element.help);
const markdownDocumentation = new vscode.MarkdownString();
markdownDocumentation.supportHtml = true;
markdownDocumentation.appendMarkdown(element.help);
item.documentation = markdownDocumentation;
if (element.text.includes('Void.')) { //If text includes Void this should be a variable
item.kind = vscode.CompletionItemKind.Variable;
}
else {
item.kind = vscode.CompletionItemKind.Method;
}
items.push(item);
});
return items;
}
});
The items gets added to the view, but I can't figure out how to 'filter' what is shown.
The official example on how to achieve this can be found here:
https://github.com/microsoft/vscode-extension-samples/blob/main/completions-sample/src/extension.ts
But this only explains how to filter based on the text/name, and i cant filter this specifically for each variableName i use.. If i somehow could detect what kind of Variable i'm working on I could possibly create a function that fetches if its a String/Int, then parse through my file and add methods in my 2nd CompletionItemProvider. But I havent found any good way of deciding the type of variable..
What i want is this:
If i click ctrl+space i want toInteger() to be the only thing that shows up, but instead it lists up everything all the time:
Anyone have a clue how to achieve this?

AG-Grid - Show sort icon without reloading data

I have an angular application and when I load my page the columns are not yet defined.
The columns are defined when I retrieve the data (which are already sorted).
So if I apply a state on my sorted column (after the data are initialized), the data are refreshed again and I want to avoid this.
Is there a solution to show the sort icon without AG-Grid refresh the data ?
To be more explicit, here is my angular code :
getRows(params: any): void {
//retrieve my data
this.myservice.getAll(params.currentPage, params.pageSize, params.sort).subscribe((data) => {
//send my datas to AG-Grid
params.successCallBack(data.results, data.totalItems);
if (params.currentPage == 0 && this.firstLoad) {
//my datas contain the columns definition
this.initGrid(data.viewParams);
}
});
}
initGrid(viewParams: ViewParamsModel): void {
this.firstLoad = false;
this.dataGrid.setColumnDefs(viewParams.columns);
//I try with this but it reloads my data
this.gridColumnApi.applyColumnState({
state: [
{
colId: 'name',
sort: 'asc',
}],
});
}
When doing this.dataGrid.setColumnDefs(viewParams.columns)
add sort: 'desc'/'asc'
columnDefs: [
{
field: 'Column1',
sort: 'desc', // <====== add this
},

How to set or remove map markers in dart/flutter

I'm trying to place and remove markers on a map widget based on the user's 'active' status in Firebase:
Map <MarkerId, Marker> markers = <MarkerId, Marker>{};
.
.
.
markerStream.listen((List<DocumentSnapshot> documentList) {
documentList.forEach((DocumentSnapshot document) {
bool activeField = (document.data() as dynamic)['active'];
final GeoPoint point = (document
.data() as dynamic)['position']['geopoint'];
final uid = (document.data() as dynamic)['uid'].toString();
//if field 'active' in firebase is set to true or false
activeField == true ? _addMarker(point.latitude, point.longitude, uid)
: _removeMarker(uid);
});
}
remove method:
_removeMarker( String uid) {
// checks uid is in markers.values map, if there, remove from collection , if not,
//I need a way to put continue or ignore in the `orElse` parameter instead of null, because the markerId cannot be null:
Marker? _markers = markers.values.firstWhere(
(thisMarker) => thisMarker.markerId.value == uid ,
orElse: () => null);
setState(() {
markers.remove(_marker);
});
}
This code runs when I omit the orElse parameter. markers will appear if all entries in firebase is set to true at runtime, and can goes away when switched to false. Problem is when the entries are already in 'false' before running, causes a Bad Element error because its still not stored in the collection. Or switching back to false from true will not remove marker from the map widget.
I installed and imported package:collection
Used .firstWHereOrNull and added .markerIdin as key (which I forgot)
Marker? _marker = markers.values.firstWhereOrNull(
(Marker thisMarker) => thisMarker.markerId.value == uid );
markers.remove(_marker!.markerId);
setState(() {
});
}