I have been trying to add an button to the end of a listview builder. I tried to do what was suggested in this question: Flutter: How to add a button widget at the end of a ListView.builder that contains other type of widgets?. But if i do that i get: "RangeError (index): Invalid value: Not in inclusive range 0..49: 50
I tried looking at questions that also had this problem but i couldnt find an anwser that fixed it.
List<Anime> animeList = animeData.animeList;
print(animeList.length);
return ListView.builder(
padding: EdgeInsets.only(bottom: 30, top: 10),
itemBuilder: (context, index) {
Anime anime = animeList[index];
if (index == animeList.length) {
return ThinOutlineBtn(
primaryColor: themeWizard.getPrimaryColor(),
darkColor: themeWizard.getBackgroundColor(),
text: "More",
highlightedBorderColor: themeWizard.getPrimaryColor(),
ontap: () {
AnilistApi.followUpQuery(AnilistApi.animeQuery, animeData,
animeData.animePage.currentPage + 1);
},
);
}
return AnimeCard(
anime: anime,
darkMode: themeWizard.getCurrentMode(),
activeIcon: themeWizard.getPrimaryColor(),
background: themeWizard.getCardColor(),
btnHighlightColor: themeWizard.getBackgroundColor(),
textColor: themeWizard.getCardTextColor(),
inActiveIcon: themeWizard.getIconColor(),
);
},
itemCount: animeList.length + 1,
);
Remove the below line and add it after if condition.
Remove this line:
Anime anime = animeList[index];
And the removed line after if condition
if (index == animeList.length) {
....
.....
}
/// Add removed line here
Anime anime = animeList[index];
You are using
itemCount: animeList.length + 1,
You should use
itemCount: animeList.length,
In ListView, indexes starts with ZEROES, not ONE
Related
I'm trying to load data from my database using Getx in flutter. I have a list of categories containing a list of sub-categories, and each sub-category has a list of products inside of it. The problem is that the UI of the sub-categories page keeps getting confused which list of data to show, since I'm calling data by category_id. Something that made my app UI unstable and always changing data.
Here is my code for the sub-categories page:
GetBuilder<SubCategoriesController>(builder: (subCategories) {
Get.find<SubCategoriesController>()
.getSubCategories(widget.catId);
print(Get.find<SubCategoriesController>()
.getSubCategories(widget.catId));
return GridView.count(
crossAxisCount: 2,
shrinkWrap: true,
physics: ScrollPhysics(),
mainAxisSpacing: 16,
crossAxisSpacing: 16,
childAspectRatio: 90 / 100,
padding: EdgeInsets.all(16),
children: List.generate(subCategories.subCategoriesList.length,
(index) {
return _buildSingleSubCategory(
index,
SubCategoryModel.fromJson(
subCategories.subCategoriesList[index]));
}),
);
})
I tried to print the output of
Get.find<SubCategoriesController>()
.getSubCategories(widget.catId)
and I figured out that it keeps running continuously without stopping showing: Instance of 'Future<void>'
, and each time it shows data of a specific category, which is pretty weird!
Edit:
Future<void> getSubCategories(int cat_id) async {
Response response = await subCategoriesRepo.getSubCategoriesList(cat_id);
//print(response.body);
if (response.statusCode == 200) {
_subCategoriesList = [];
_subCategoriesList.addAll(response.body);
_isLoaded = true;
update(); // = setState();
}
}
I found out that the update() function called in here is making the issue, but once I remove it I get no data at all.
I think the problem is you are trying to call your getSubCategories(widget.catId) from your builder method.
You can try resolving this issue by calling your getSubCategories(widget.catId) from initstate instead of in builder method like this:
GetBuilder<SubCategoriesController>(
initState: (logic) => logic.getSubCategories(widget.catId),
builder: (subCategories) {
return GridView.count(
crossAxisCount: 2,
shrinkWrap: true,
physics: ScrollPhysics(),
mainAxisSpacing: 16,
crossAxisSpacing: 16,
childAspectRatio: 90 / 100,
padding: EdgeInsets.all(16),
children: List.generate(subCategories.subCategoriesList.length,
(index) {
return _buildSingleSubCategory(
index,
SubCategoryModel.fromJson(
subCategories.subCategoriesList[index]));
}),
);
},
)
I am using flutter_multi_select_items for multiple items selected from an array of items in JSON format. It is working before but I will like to restrict users to only select items in the order from right to left and not jump to any item to pick the next. For example if I have an array [1,2,3,4] I will like user to select in order 1->2->3->4 only and not pick like 1->3->2->4 etc.
My code:
MultiSelectContainer(
itemsPadding: const EdgeInsets.all(10),
textStyles: const MultiSelectTextStyles(
textStyle: TextStyle(fontFamily: 'Montserrat',
fontWeight:FontWeight.bold,)),
showInListView: true,
listViewSettings: ListViewSettings( scrollDirection: Axis.horizontal,
separatorBuilder: (_, __) => const SizedBox(
width: 10,)),
items: List.generate(dataSchedule == null ? 0 : dataSchedule.length,
(index) => MultiSelectCard(value: dataSchedule[index]['time_slot'], label: dataSchedule[index]['time_slot'],)),
onChange: (allSelectedItems, selectedItem) { })
Below is what my array return looks like:
["16:00","17:00","18:00","19:00","20:00","21:00"]
And I will like selection to be from 16:00 to 17:00 to 18:00 to 19:00 to 20:00 to 21:00
From the above code allSelectedItems is for an array of items selected and selectedItem is just for a single item selected. If there is any part of clarification needed please let me know, thanks in advance.
So I was able to solve this by applying some logic to achieve this. Since I can't get index inside onchanged I added to my value: the index so I can get the index value of each select, then I later removed the extra value from the value and leave only the index value and then get the previous select value and current selected value. Finally, I used if statement to check if the current select minus 1 is equal to the previous select then it is correct or else not. My final code is as below:
MultiSelectContainer(
controller: controllerMultipleSelect,
itemsPadding: const EdgeInsets.all(10),
showInListView: true,
listViewSettings: ListViewSettings(
scrollDirection: Axis.horizontal,
separatorBuilder: (_, __) => const SizedBox(
width: 10,
)),
items: List.generate(dataSchedule == null ? 0 : dataSchedule.length,
(index) => MultiSelectCard(
value: index.toString() +"-"+ dataSchedule[index]['time_slot'], label: dataSchedule[index]['time_slot'],)),
onChange: (allSelectedItems, selectedItem) {
if(allSelectedItems.length>1) {
var lst = allSelectedItems[allSelectedItems.length - 2];
var pos = lst.toString().lastIndexOf('-');
String previousResult = (pos != -1)? lst.substring(0, pos): lst;
var pos1 = selectedItem.toString().lastIndexOf('-');
String currResult = (pos1 != -1)? selectedItem.substring(0, pos1): selectedItem;
if(int.parse(currResult)-1 == int.parse(previousResult)){
debugPrint("Correct order");
}else{
debugPrint("Incorrect order");
}
}
})
I am facing an issue when update a copy RxList and modify some field then original data also change.
Please you help me, how to make change without effect to the original data?
This mean that, when I click on checkBox then copyList elemnt change to TRUE and not affect to drniks list but current it changes both.
#mock data
List<Drink> drinks = [
Drink(id: 0, name: "Coca Cola",isChecked: false),
Drink(id: 1, name: "Milk",isChecked: false),
Drink(id: 2, name: "Soda",isChecked: false),
];
I want to build a checkbox by listing these above mock data
var drinkIds = <int>[];
var copyList = drinks;
#override
Widget build(BuildContext context) {
return Obx(()=>ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: copyList.length;
itemBuilder:(_,index)=>ListTitle(
minLeadingWidth: 10,
visualDensity: VisualDensity(vertical: -4),
contentPadding: EdgeInsets.zero,
title: Text("${copyList[index].title}"),
leading: copyList[index].isChecked ? Icon(Icons.check_box_outlined, color: primaryColor) : Icon(Icons.check_box_outline_blank, color: Colors.grey),
onTap: () {
copyList[index].isChecked = !copyList[index].isChecked;
if (copyList[index].isChecked) {
drinkIds.add(copyList[index].id);
} else {
drinkIds.removeWhere((element) => element == copyList[index].id);
}
copyList.refresh();
},
)
));
}
You can copy list by spread operators
List newList = [...drinks];
Map newMap = {...myMap}; // this is same for maps
The newList is copy of drinks and now you can work with it without effects on original
read this article
read about dart feature
Let me Explain,
Suppose I have a Listview.builder with 3 item
and I Want to change the data of item no.2 without changing the other items data(1 and 3).
And I have two data sources for updating on those 3 items
according to button press.
Please Help me if you have any idea to solve this problem.
Thank You
Welcome #Rahul Choudhury!
ListView.builder has the property itemBuilder who accepts the index argument.
You can use that one!
final items = List<String>.generate(3, (i) => "Item $i");
const int specialIndex = 1;
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
if (specialIndex == index){
//Use here your custom amazing widget
return const ListTile(
title: Text("Flutter is awesome"),
);
}else {
return ListTile(
title: Text(items[index]),
);
}
},
);
Obviously, I suggest you to refactor this code as you like, but I wanted to give you an idea ;)
I have a positioned ListViewBuilder inside a Stack attempting to show a dynamic drop down across multiple tiles with different drop down values. This seems to work fine, except for the fact that the values don't seem to clear correctly when the list is updated with new values and refreshed on SetState.
ListView builder code
Positioned(
top: 100,
left: 100,
width: 90,
height: MediaQuery.of(context).size.height,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: dropDownListValues.length,
itemBuilder: (context, index) {
return Tile(
tileId: dropDownListValues[index],
isHeaderTile: false,
uuid: Uuid().v1(),
);
},
),
),
Clear and update list based on which button is pressed.
void _toggleDropDown(List valueList) {
if (dropDownListValues.isEmpty) {
setState(() {
dropDownListValues = valueList;
});
} else {
setState(() {
dropDownListValues.clear();
});
}
}
What I end up getting is the list extends based on the number of items in the drop down, but the values from the previous list carry across from the last list..
Example.
Drop Down list values
Dropdown list 1 = ['one', 'two']
Dropdown list 2 = ['two', 'three', 'four']
What I am seeing is
Dropdown list 1 = ['one', 'two']
Dropdown list 2 = ['one', 'two', 'four']
The if I click on list two drop down first I get the following
Dropdown list 1 = ['two', 'three']
Dropdown list 2 = ['two', 'three', 'four']
Im stumped and have spend hours on this, any idea what I could be doing wrong to cause this refresh issue?
Thanks in advance.
Try using keys so the Flutter Framework can identify changes to your list.