My Question: Can you keep built UI that was built by a state when another state is called?
Please correct me if I'm using the cubit pattern wrong. I have a unique example I'm trying to solve. I have the cubit set up and it's working as per documentation.
I have a screen where I have a horizontal list that is returned through cubit. Then based on the horizontal list returned if you click one of the items the cubit fetches a second list to display vertically underneath the horizontal list.
var _responseCategories = await _repository.postGetRootCategories();
emit(ShowCategories(state, _responseCategories));
var _responseCategoryItems = await _repository.postGetCategoryItems(_responseCategories[0].id);
emit(ShowCategoriesItems(state, _responseCategoryItems));
The ui has a bloc builder and processes the two states but when the one state is changed the UI for the first built state is then not there I understand why this happens but is there a way to stop it until I emit the state again. The UI decodes the states in a builder as follows:
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
if (state is ShowCategories) buildCategories(context, state),
state is ShowCategoriesItems
? Expanded(child: buildMenuItems(context, state))
: SizedBox.shrink(), //getMenuList(_activeSelection)),
],
);
If I'm using this wrong that's understandable I just want to know if there is a better solution to this to solve the problem as the category items are different per category and I don't need to fetch the categories every time the state changes.
The app is essentially a menu. The horizontal section is the categories of the menu 'I only need to load them once' and the vertical section is the items 'I need to load them when the user selects a category'. The items are different per category but the categories never change unless set by the server.
It seems like you have a master-detail view and your detail state is just a superset of the master-no-detail state.
You could derive your detail state from the master state and add more information to it (master-list, adding selected, details-list to the derived class) or you could just go with one state for both (master-list, selected, details-list) where some things aren't set when nothing is selected yet.
If your details view is really complicated, you could also create a whole new BLoC for it, with it's own logic and builder in the widget tree, then you can have totally different states.
But states from the same BLoC, you cannot opt to only rebuild a part of the tree below the builder.
(well, technically you can do anything, but there is no point in working around a pattern. If it does not fit, use a different one. And I think you can easily make it fit as described above)
Related
for RiverPod official todos sample, system triggers build function whenever I toggle checkbox item, is it a correct behavior ? Won't it cause performance issue? thanks.
As I know, state management has one purpose to avoid rendering widgets so often. Please correct me if any.
if you are watching your provider inside your build method then yes it rebuilds your entire build method. but if your want your the check box to only rebuild when necessary then its either (1) separates that into another class or (2) wraps that check box with Consumer Widget. something like:
Consumer(
builder(_, ref, __){
final checkboxProvider = ref.watch(yourCheckboxProvider);
return CheckBox();
}
)
I'm building a chat app and I want to achieve a ListView() that loads up like its reverse property is set to true i.e showing the end of the ListView on build. The reason I don't want to set the reverse property to true is because, if the items in the ListView do not fill up the screen, they are aligned to the bottom (cannot use shrinkWrap because of performance), and also because it reverses the order of the items and to show new elements at the bottom of the list, I'd need to use a spread operator and I'm worried about performance since the Map from which I render from can grow significantly.
I have also tried using the ScrollController's jumpTo method inside a SchedulerBinding.instance!.addPostFrameCallback but the user can see the scrolling and
it also causes it to jump like it's attempting to scroll beyond what is contained in the ListView. I have also used ScrollablePositionedList and set the index to which I want to scroll, but this jumps also.
Is there any way I can achieve my aim without any drawbacks visually or performance-wise?
You have the correct idea: in the initState, jump to the end. The end of the list can be obtained from position.maxScrollExtent. When done correctly, this does not create any visual or performance drawbacks. For example:
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_controller.jumpTo(_controller.position.maxScrollExtent);
});
}
The _controller should be the ScrollController passed to the list.
I'm new to Flutter and I'm trying to build a chat application and I've watched several tutorials. To view the chat messages list/history, almost every tutorial is doing something like this: (I'm shortening the code to get to the point)
List<Widget> messages = api.listOfMessages();
return Column(children: messages);
Now every time there's a new message, messages is updated and the column is re-built. I gotta say the word "rebuild" sounds an expensive procedure to me. Say 2 users have been chatting 500 lines. Now every time a new message is coming, 500 lines are getting rebuilt over an over.
I thought about putting an empty widget at the end of the list. So when a new message arrives, I just insert it to that empty widget and have that to rebuild only:
List<Widget> messages = api.listOfMessages();
return Column(children: [...messages, EmptyWidgetForNewMessage()];
But that looks like a hack and will cause a lot of nested widgets, because every new message must also insert another EmptyWidget etc...
How can I avoid rebuilding previous messages and only insert the new one to the view? (or rebuilding the entire list is not that big a deal?)
You can use sliver widget to build the messages that are visible in viewport(or within cacheExtent).
Like ListView.build, ListView.separated, from the doc of ListView.build:
if the list view's children are
created in advance, or all at once when the [ListView] itself is created,
it is more efficient to use the [ListView] constructor. Even more efficient, however, is to create the instances on demand using this constructor's itemBuilder callback.
Also, we will not fetch all messages from server at once. Instead we will fetch them in batches with query like ?page=1&size=20.
Note: There is a known issue with this widget, avoid using shrinkWrap: true if possible. See this issue
couple of improvements that you could target
Use ListView builder constructor instead of the column, only the children widget that are currently visible in the screen will be rendered whereas in Column widget all of its children will be rendered. Also ListView should be your preferred widget, because the Column widget is not scrollable, and there are chances of overflow exception, if the message list length is huge.
Use const constructor for all type of widgets returned by api.listOfMessages(), this will allow the compiler to reuse any rendered widget, meaning every time when a state change happens(in you case arrival of a new message) the entire tree is not re-rendered, the renderer will have the luxury of re using previously built message widget.
These two suggestions should take care of any performance bottlenecks, in short we would be rendering only the visible children widgets with ListView and we will be re reusing already rendered widget with the help of const constructor.
Currently, I have a list view that allows items within the list view to be dismissable. I want each item within that list view to correspond to an element within another list that contains text. And when that widget in the listview is dismissed, then the corresponding element in the other list also gets deleted. Does anyone know hot
What I suggest is that if there is any common thing in both list that are related to each other such as id or any else say text in your case ,After dismissible widget get triggered iterate via the second list just check if there's any common based on the 1st list item, and delete the item rebuild the state. Maybe adding some code might be better but so far this is the best way you can do. let me know if it work.
I'm fighting with Flutter's provider and can't understand all of it's possibilities.
The thing is that for example I have such widgets structure:
Widget_A
--Widget_B
----Widget_C
----Widget_D
--Widget_E
--Widget_F
Simple example is TODO list. Imagine you have categories, add_field and todos_list. Each is a separate widget. Than you change categories so todos_list should be reloaded to show todos from selected category. Later you add todo using add_field so todos_list also need to be updated because of new record. How can I achieve something like this?
PS: I was trying to separate all the stuff to the different Provider classes, but effect is the same: if widget uses Provider.of(context) it will be updated no matter what. And as I should combine data from 2 providers there will be widget that is connected to the both providers so the result will be endless loop. Still.