Closed Lix-ai closed 3 years ago
Like using for example audioplayers package and run several players.
Hi @Lix-ai 👋 Thanks for opening an issue!
How to add item for a list without the need of rebuilding all list.
Generally I wouldn't worry about this unless you are experiencing performance issues. Widgets like ListView.builder
are already optimized to only render the visible portions of the list.
How to update a class instance property value. All state should be immutable so the way to change state is to create a new instance of the state. I recommend using
copyWith
as illustrated in https://github.com/felangel/bloc/blob/32dbf13baff0cbd10bf47e5052ccf25903166d19/examples/flutter_firebase_login/lib/login/cubit/login_state.dart#L17.Does BLoCProvider used only once in a class?
You should generally use a BlocProvider once for each bloc instance when you want to create and provide the bloc instance to a part of the widget tree.
Does i need to bring always a list in order for example to update an item from the list or only changing the state+emit(in cubit) will change the state of the application, maybe it realted to BLocListener?
As I previously mentioned, the way to update a state or part of a state is to emit a new instance of the state. You can use a copyWith
method to create a new instance with some modifications. At the end of the day, it's totally up to you how to model the state and it will vary based on the feature or use-case.
Hope that helps! 👍
Thanks for the detailed answer Felix, great honor to hear from the creator of the most trended flutter architecture (i spend a month for looking of the correct architecture approach) =],
I would like to summarize/asked a questions regarding your answer:
===> It seems that any changing the application is including rebuilding all widgets (from scratch w/o property value changing) without preserving state of the application (state-machine attitude).
Thanks in advance and thanks for all :)!!!
Hi @Lix-ai 👋
BlocListener
is used to react to state changes in the bloc and it's useful for side effects like navigation, showing dialogs, etc. It is not directly used to update/emit new states but you can accomplish that by adding an event to the bloc from the listener. Read more about it here;BlocProvider
can be provided pretty much anywhere in the widget tree and any widget that is a descendant will have access to the provided instance of the bloc. If by view you're referring to a route then BlocProvider
can be used to provide a bloc to the whole route, but it can also be used to scope a bloc to multiple routes or even provide the bloc to all routes if placed above the MaterialApp
. Read more about it here;StatefulWidget
when using bloc, you can build UI with StatelessWidget
s only. But if you need to handle some additional UI logic like animations then there's no inconvenience in just converting your widget to a stateful one, bloc specific widgets will behave exactly the same. The bottom line here is that you don't need a StatefulWidget
to preserve state because state is already preserved by bloc being cached by BlocProvider
between rebuilds.Hope that helps! ✌
Hi and thanks @narcodico 👋 All clear except question number 3.
Still don't understand if a have audioplayer in an item which part of a list (ListView.Builder, for example) and I want to use Bloc? Can I achieve it? You said "Only keep the non-UI data/parameters on the state class", but again if i want to add new item which contains an audioplayer, what about other audioplayers at other item?
I.E, I need to add item to the list only via the 'Cubit'. In "https://github.com/felangel/bloc/tree/master/examples/flutter_complex_list/lib/list" example, you can see that the 'State' class send the list data to the view (UI class). Confused.
Thanks =]
Starting from the example you linked let's assume the Item
class holds the values needed to build an audio player.
class Item extends Equatable {
const Item({
@required this.url,
});
final String url;
Item copyWith({
String url,
}) {
return Item(
url: url ?? this.url,
);
}
@override
List<Object> get props => [url];
}
Then you can use the state of the cubit to build a list of audio players.
ListView.builder(
itemBuilder: (BuildContext context, int index) {
return AudioPlayer(
item: items[index],
);
},
itemCount: items.length,
);
And your AudioPlayer
is a custom widget something like:
class AudioPlayer extends StatelessWidget {
const AudioPlayer({Key key}) : super(key: key);
final Item item;
@override
Widget build(BuildContext context) {
return SomeAudioPlayerWidgetFromSomePackage(
url: item.url,
);
}
}
So the difference is that you're only storing in the state the data needed to build the UI but not the UI itself, e.g.: url
string not AudioPlayer
widget.
Thanks and sorry for late replay, So with that I can achieve what i desired to and keep the state of the items as they are right (after adding/deleting items)?
Thanks =]
No worries and yup that's correct 👍
Hello, I'm not sure it is correct to write into a closed issue, but since it is related I'll give a shot here first. I recently stumbled on the need for this and I wanted to know your opinion on this. I have a list with some items fetching an url preview when the text contains an url. However the list being rebuild each time, the animations that expand the item list once the data is ready trigger every time an item is added and therefore the list rebuilt. Can I avoid this behavior using BloC ?
Hello, I'm not sure it is correct to write into a closed issue, but since it is related I'll give a shot here first. I recently stumbled on the need for this and I wanted to know your opinion on this. I have a list with some items fetching an url preview when the text contains an url. However the list being rebuild each time, the animations that expand the item list once the data is ready trigger every time an item is added and therefore the list rebuilt. Can I avoid this behavior using BloC ?
There are many paths to sorting out this problem, although following the suggested BLoC approach may greatly help you understand what the problem is and how to best solve
Hey, thank you for your reply.
Actually the problem is not related to bloc at all. In fact
ListView.builder
will always rebuild all of its children, independently
to setting a key for them.
The solution I found is to use a ListView.custom
with a
SliverChildBuilderDelegate
. Then assigning a key to its children and
retrieving the key from the index works as expected and child is not
rebuilt. Not that childCount
and findChildIndexCallback
are parameters
of the sliver and not the list itself.
Here is an example:
ListView.custom(
physics: const AlwaysScrollableScrollPhysics(),
childrenDelegate: SliverChildBuilderDelegate(
(context, index) {
return MessageWidget(
key: ValueKey('m-${state.messages[index].messageId}'
),
message: state.messages[index],
);
},
childCount: state.messages.length,
findChildIndexCallback: (key) {
final ValueKey<String> valueKey = key as ValueKey<String>;
final index = state.messages
.indexWhere((m) => 'm-${m.messageId}' == valueKey.value);
return index;
},
),
),
Oye, gracias por tu respuesta. En realidad, el problema no está relacionado con el bloque en absoluto. De hecho
ListView.builder
, siempre reconstruirá a todos sus hijos, independientemente de establecer una clave para ellos. La solución que encontré es usar aListView.custom
con aSliverChildBuilderDelegate
. Luego, asignar una clave a sus elementos secundarios y recuperar la clave del índice funciona como se esperaba y el elemento secundario no se reconstruye. No esochildCount
yfindChildIndexCallback
son parámetros de la astilla y no de la lista en sí. Aquí hay un ejemplo:ListView.custom( physics: const AlwaysScrollableScrollPhysics(), childrenDelegate: SliverChildBuilderDelegate( (context, index) { return MessageWidget( key: ValueKey('m-${state.messages[index].messageId}' ), message: state.messages[index], ); }, childCount: state.messages.length, findChildIndexCallback: (key) { final ValueKey<String> valueKey = key as ValueKey<String>; final index = state.messages .indexWhere((m) => 'm-${m.messageId}' == valueKey.value); return index; }, ), ),
Could you show a hello world? I try to explain your code but always when I add a new element to the list it repaints all the elements of the list
well it was a long day so i just describe what i want as good as i can. its actually simple. i have an infinite List, no Stream, i'm fetching data with offset and limit.
if i tap on the item in List and edit the title, it should update only this one item in List after saving it. (or add a new one visible to list not somewhere hidden in db) so should i use Bloc for List overview and wrap each List | Item in another Bloc? is it to much? will it even work
it bothers me for quite a while now, i always succesfully avoided it. What would be the better approach with flutter_bloc to solve it.
Maybe my question is a nooby one, but i really try to understand how to implement the following operations:
How to add item for a list without the need of rebuilding all list. How to update a class instance property value. Does BLoCProvider used only once in a class? ===> I'm asking those question because i see that BLoC working with 'Equatable' and that in a the state class there is always a need to bring default value to the state we provide to the view class.
Another Question (what change the view and operate the state): Does i need to bring always a list in order for example to update an item from the list or only changing the state+emit(in cubit) will change the state of the application, maybe it realted to BLocListener?
Thanks!