Thank you for this wonderful lib.
I have a question regarding bloc. take for example this client bloc. this is a simple bloc provider.
class ClientBloc extends Bloc<ClientEvent, Client> {
String _id;
@override
Client get initialState => Client();
String getId() {
return _id;
}
void setId(String id) {
_id = id;
}
@override
Stream<Client> mapEventToState(ClientEvent event) async* {
switch (event) {
case ClientEvent.get:
Response res = await Web.getClient(_id);
yield Client.fromJson(res.data);
break;
case ClientEvent.newly:
_id = null;
yield Client();
break;
case ClientEvent.post:
Response res = await Web.addClient(state.toJson());
yield Client.fromJson(res.data);
break;
case ClientEvent.put:
Response res = await Web.updateClient(state.toJson());
yield Client.fromJson(res.data);
break;
}
}
}
enum ClientEvent { get, newly , post, put }
I have a need to also output a list of clients. say a ClientEvent.list and in the mapEventToState add something like Web.getClients() which output List
So far this works for me. And i did not put much thought into using a repository.
Is it possible to have these 2 here? or do i have to create a separate bloc provider that outputs a List
Please advise.
Hi @chitgoks 馃憢
I'd advice you to extend your Client state class to contain current data but also your needed list.
Blocs should not contain additional state but rather everything that is considered state should go inside state class/es. So your String _id; field should be transferred to the Client.
You might also find useful this infinite list example
Hope this helps you better understand the concept of bloc 馃憤
thanks that link helps. ill look into it and get back
i started out the wrong way it seems. so the logic is still like how to get and set state in redux.
Is the Client the current user of your application ?
How you come up with what blocs to use and where, depends on what exactly your needs are and how your screens should look like. If you could share more details about this I could then advice you better.
i have a list of clients in 1 screen. so i will add an event.list where it will get from api Web.getClients().
then once i select a client in the list, it should get the client details (from api too) which is why i have a setId() so that when event.get is called, it will get Web.getClient(id)
the blocprovider add() only adds events. it does not have some param data where i can set the id. thoughts?
I went ahead and did it like this.
class ProspectBloc extends Bloc<ProspectEvent, ProspectState> {
@override
ProspectState get initialState => ProspectState();
@override
Stream<ProspectState> mapEventToState(ProspectEvent event) async* {
switch (event.action) {
case ProspectAction.get:
Response res = await Web.getProspect(event.data);
state.prospect = Prospect.fromJson(res.data);
yield state;
break;
case ProspectAction.list:
state.list = List<Prospect>();
Response res = await Web.getProspects();
res.data.forEach((element) => state.list.add(Prospect.fromJson(element)));
yield state;
break;
case ProspectAction.newly:
state.prospect = Prospect();
yield state;
break;
case ProspectAction.post:
Response res = await Web.addProspect(state.prospect.toJson());
state.prospect = Prospect.fromJson(res.data);
yield state;
break;
case ProspectAction.put:
Response res = await Web.updateProspect(state.prospect.toJson());
state.prospect = Prospect.fromJson(res.data);
yield state;
break;
}
}
static ProspectEvent get(String data) {
ProspectEvent event = ProspectEvent();
event.action = ProspectAction.get;
event.data = data;
return event;
}
static ProspectEvent list() {
ProspectEvent event = ProspectEvent();
event.action = ProspectAction.list;
return event;
}
static ProspectEvent newly() {
ProspectEvent event = ProspectEvent();
event.action = ProspectAction.newly;
return event;
}
static ProspectEvent post() {
ProspectEvent event = ProspectEvent();
event.action = ProspectAction.post;
return event;
}
static ProspectEvent put() {
ProspectEvent event = ProspectEvent();
event.action = ProspectAction.put;
return event;
}
}
class ProspectState {
Prospect prospect = Prospect();
List<Prospect> list = List<Prospect>();
}
enum ProspectAction { get, list, newly , post, put }
class ProspectEvent {
ProspectAction action;
String data;
}
One thing though. is that when list is called, it does not rebuild the widget. this is the code.
@override
Widget build(BuildContext context) {
return BlocBuilder<ProspectBloc, ProspectState>(
builder: (context, state) {
print('list');
return Container();
}
);
}
At first run of the widget, print('list') runs and state.list is empty. Once the async finishes getting the list from the api, it does not rebuild the widget.
thoughts?
You can pass payload to your event so you could add an event like LoadClientRequested(clientId) which you can then translate to your api call.
It is also inadvisable to mutate the state so instead of state.prospect = Prospect.fromJson(res.data); you should go with yield state.copyWith(prospect: Prospect.fromJson(res.data));
hi @RollyPeres thanks for getting back. sorry but i have to ask. copyWith() doesnt exist. and this is available only in widgets.
Hi @chitgoks copyWith is not built in to your state class.
You have to manually do it on your own
class Todo{
final String title;
final int id;
final String description;
Todo({this.title,this.id,this.description});
Todo copyWith({String title, int id, String description}){
return Todo(
id: id ?? this.id,
title: title ?? this.title,
description: description ?? this.description
);
}
}
thank you for the help @RollyPeres @ram231
Most helpful comment
Hi @chitgoks copyWith is not built in to your state class.
You have to manually do it on your own