Bloc: Question on Blocs

Created on 12 Jun 2020  路  9Comments  路  Source: felangel/bloc

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 but this bloc is limited to outputting a Client object only.

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.

question

Most helpful comment

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

All 9 comments

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ricktotec picture ricktotec  路  3Comments

RobPFarley picture RobPFarley  路  3Comments

Reidond picture Reidond  路  3Comments

timtraversy picture timtraversy  路  3Comments

craiglabenz picture craiglabenz  路  3Comments