Flutter GridView ItemBuilder - flutter

I'm building a GridView based on data stored in a Hive box. The data stored in Hive has several objects to it and i need to get to some of that data in order to display it in a GridView.
I have a couple of issues with building the GridView, one i get an issue when first opening the page, where no data is loaded and then i get data after a few seconds, the other issue is that what i've written can't be correct as it just looks too badly written.
This is my code for the GridView builder;
Builder(builder: (context) {
return GridView.builder(
padding: EdgeInsets.zero,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 0,
mainAxisSpacing: 0,
childAspectRatio: 1,
),
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: posList?.length,
itemBuilder:
(context, posIndex) {
PointOfServices? pos = posList
?.elementAt(posIndex);
List<List<Orders>?> ordering = posList!.map((e) => e.orders).toList();
List<Orders>? ordering2 =ordering.elementAt(posIndex);
Orders? ordering3 = ordering2?.elementAt(0);
As you'll see there are several Lists etc to eventually get me to the data i require, it's just not best practice at all, i'm new to Flutter etc :)
This is how i get the data for the posList;
void getPosData() async {
final posData = _orderInfo?.values
.where((posDataMap) =>
posDataMap.deliveryAddressNo == deliveryAddressNo)
.toList();
posDataMap = posData?.asMap();
var posList1 = posDataMap?.values.first.pointOfServices?.toList();
posList = posList1
?.where((element) =>
element.pointOfServiceOrderingGroupNo == posGroupId)
.toList();
}
_orderInfo being my Hive Box data. Data is brought in to the app via an API and stored to my Hive Box.
Do i need to wait for my data to build first before loading the page? Any pointers and feedback would be great.
Thanks

Related

Flutter getx loading data continuously causing UI to be unstable about which data to show

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]));
}),
);
},
)

How to make flutter listview show millions of items

I'm tring to write a log viewer application with flutter.
I've learn that ListView.builder could handle larget number, even infinite number of items.
for example:
const mockLines = [
'this is a short line that will display in one line',
'this is a long line that will display in multiple lines, let us see how long it will be ?',
];
Widget _buildLogView() {
return ListView.builder(
itemCount: 1000,
itemBuilder: (context, index) {
final line = mockLines[index % mockLines.length];
return Text('$index: $line');
}
);
}
which works good, text got wrapped when it's too long.
But scrolling got very slow, and memory got blow when I set itemCount to 1000000.
Tried itemExtent:
Widget _buildLogView() {
return ListView.builder(
itemCount: 1000000,
itemExtent: 20
itemBuilder: (context, index) {
final line = mockLines[index % mockLines.length];
return Text('$index: $line');
}
);
}
Problem solved.
However, text won't wrap anymore, it just got truncated.
It seems that there's no way for me to create a listview can handle millions of lines when keeping the 'wrap' behavior.
Am I miss something, or just a limitation of flutter?
===============
A release build, draging scrollbar to 1/3 with itemCount = 1000000
memory keep crimbing up (would finally eat all)
same on windows:

why the GridView.custom did not rerender in flutter

I am uisng GridView.custom to render four Grid in the sreen in flutter(v3.0.4), this is the core render dart code look like:
Widget buildGridView(List<TodoTask> renderTasks) {
return GridView.custom(
semanticChildCount: 2,
cacheExtent: 4,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
mainAxisSpacing: 3.0,
crossAxisSpacing: 3.0,
crossAxisCount: 2,
childAspectRatio: childAspectRatio.value,
),
childrenDelegate: SliverChildBuilderDelegate((BuildContext context, int index) {
List<TodoTask> itemsBuild = renderTasks.where((element) => element.priority == index + 1).toList();
return buildListViewItemWidget(index, itemsBuild);
}, childCount: 4),
);
}
the render function will be invoke 2 times, fisrt the page initial render, the tasks size are 0. The next time when the async request fetched the tasks from server side, invoke this function to rerender the widget. To my surprise, the second time execute but did not trigger the GridView.custom rerender. why did this happen? Am I missing something? what should I do to let the GridView.custom rerender after fetched data from server side? I set the breakpoint in childrenDelegate code block, the sencond invoke enter the buildGridView function but did not trigger the breakpoint in childrenDelegate. I have tried like this:
Widget buildGridView(List<TodoTask> renderTasks) {
if (renderTasks.isEmpty) {
return Text("Loading....");
}
return GridView.custom(
semanticChildCount: 2,
cacheExtent: 4,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
mainAxisSpacing: 3.0,
crossAxisSpacing: 3.0,
crossAxisCount: 2,
childAspectRatio: childAspectRatio.value,
),
childrenDelegate: SliverChildBuilderDelegate((BuildContext context, int index) {
List<TodoTask> itemsBuild = renderTasks.where((element) => element.priority == index + 1).toList();
return buildListViewItemWidget(index, itemsBuild);
}, childCount: 4),
);
}
when the renderTasks is null, render the loading..., seems still did not enter the childrenDelegate.
BTW:seems like the controller did not contains buildContext. I am put this code in the get get: ^4.3.8 controller code.
Sliverchildbuilderdelegate will build it lazily. Maybe thats the reason. Its not build till it needs to be displayed. Try other delegates like sliverchildlistdelegate

How to make this containers in flutter depending on length from api?

I try to make this column but data come form API, so i need solve to this problem ?
You can used Listview with builder
ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
ProjectModel project = snapshot.data[index];
return Column(
children: <Widget>[
// Widget to display the list of project
],
);
},
);
here snapshot.data is coming from future API for an example if your API gives list of object you can use FutureBuilder to wait until request is complete. And fetch snapshot data to ListView.builder here docs about future builder you can find clear idea from this

How to use detect changes in elements of an array using provider?

I started developing my first app with flutter and I have some questions.
I have a screen that contains a Horizontal ListView where each item represents a month and occupy the entire screen. Each item is another list of itens. It's basically a list showing expenses for every month. I'm controlling state using Provider, and I'm having trouble to detect changes in a month. For example, I'm in january, but I added an expense in february, so the february item must detect the change and rewrite. I haven't been able to do it so far. Here is the screenshots of the app:
I have a provider that has an array of "Periods". And a "Period" is a class that represents a month, and contains an array of expenses.
I guess I'm not organising my data the right away, I'd appreciate if someone could point me in the right direction.
EDIT:
So, I watched this video here: https://www.youtube.com/watch?v=d_m5csmrf7I, and that helped me a little at least on how to detect changes in nested providers. I basically did this:
Widget build(BuildContext context) {
...
final size = MediaQuery.of(context).size;
final financeiro = Provider.of<Financeiro>(context);
return ImprovedListView.builder(
controller: _listController,
scrollDirection: Axis.horizontal,
itemCount: financeiro.periodos.length,
itemSize: size.width,
physics: const NeverScrollableScrollPhysics(),
initialScrollOffset: financeiro.indexMesAtual * size.width,
itemBuilder: (ctx, index) {
final periodo = financeiro.periodos[index]; //get a piece of the list and pass to another change notifier provider
return ChangeNotifierProvider<Periodo>.value(
value: periodo,
child: PeriodoFinanceiro(
key: ValueKey('${periodo.ano}${periodo.mes}'),
index: index,
scrollTo: scrollTo,
carregarDadosIniciais: carregarDadosIniciais,
excluirLancamento: excluirLancamento,
),
);
};
}
Now I just have to figure out how to better control the scrolloffset of the list when its re-rendered.
Thanks