Let's say that I've got a simple cubit class, that exposes a stream.
class SimpleCubit extends Cubit<SimpleCubitState> {
SimpleCubit() : super(SimpleCubitState());
final _simpleStreamController = StreamController<String>();
Stream<String> get simpleStream => _simpleStreamController.stream;
}
When should I close the stream?
Is calling it in close() function of the Cubit enough?
Or should I always create custom dispose() function inside the Cubit and call it from dispose method of the Widget?
And if it's the latter, does this mean, that every time I want to expose stream from the cubit i need to make my Widget Stateful just to have a dispose function? What if I'm using it in the StreamBuilder so I never actually call listen on the stream on my own in the code?
If that's the case, this seems like a huge overkill.
Related
I'm trying to use bloc with websockets in both directions (i.e. when a message is received on the websocket, an event is fired as well as when a state is emitted, a message is sent over the web socket). I'm still fairly new to flutter but have written similar style code (message queues and websockets) in other languages but and I'm really struggling to get my head around how to structure everything so it works in flutter.
I have a basic class that opens a websocket and waits for events
class WebsocketManager {
final BuildContext context;
late IOWebSocketChannel channel;
WebsocketManager(this.context);
void connect(){
channel = IOWebSocketChannel.connect(Uri.parse(wsBaseUrl));
channel.stream.listen(
(msg) {
//process msg
BlocProvider.of<SomeBloc>(context).add(MessageReceived(msg));
}
);
}
}
This works perfectly fine (although having to pass the BuildContext in feels a bit wrong). The issue is with listening for new states. I assumed I would be able to do something like this
BlocListener<SomeBloc, SomeState>(
listener: (context, state) {
if(!sendMessage(SomeMessage()))
});
However this listener never fires. If I place that same code as a child of a Widget then it works fine so I assume a BlockListener has to be a child of a widget.
My question is, is there a way of using BlocListener (or some alternative) without being a child of a widget?
Alternatively, is there a better way I could structure my code so it can have access to both the websocket and bloc?
Thanks
First, the BlockListener must be a child of the widget being provided with the bloc.
Yeah, I wouldn't pass the BuildContext into the WebsocketManager. I'd flip it around a bit, to make it a bit cleaner.
How you do it will of course depend on how your UI should behave based on events and states. But for the sake of keeping my example simple, I have a suggestion below where a Widget (perhaps an entire route), is listening and updating based on the websocket messages. You can of course do it so that entire app is affected by the websocket, but that is "the next step".
The bloc should be the glue between your WebsocketManager and the UI. So I'd suggest to let your Bloc be created (provided) to a Widget where appropriate. The bloc holds an instance of WebsocketManager (perhaps a Singleton?). Have a method and corresponding state in the bloc that sets up the connection using WebsocketManager.connect(). But instead of doing the ....add(MessageReceived(msg)) stuff in the listen callback, have a method (perhaps it is your connect() method) that returns a Stream<type of msg> and let the listen callback yield msg. You can then in your bloc set up a StreamSubscription for the WebsocketManager's stream, and then emit states and act based on what is received from the websocket.
I'd suggest that you also convert the msg from the listen-callback to your own domain object and yield that in the stream, so that your bloc isn't strictly dependent on the msg type from the WebsocketManager.
This way your WebsocketManager only do 'websocket-stuff' in the data layer, and you let your bloc do the logic in the application layer, which in terms will let your UI update based on what you bloc emits.
I have two blocs - BlocA and BlocB. The latter adds events into the former.
class BlocA extends Bloc<BlocAEvent, BlocAState>{....}
class BlocB extends Bloc<BlocBEvent, BlocBState>{
final BlocA blocA;
....
Stream<BlocBState> mapEventToState(BlocBEvent event) {
if(event is NotifyBlocA) blocA.add(FromBToAEvent());
}
}
Everything is working fine. However, I need to write down a test that makes sure that when BlocB receives a NotifyBlocA event, BlocA does indeed receives a FromBToAEvent event. Is there a way to get the events stream of BlocA to check that? In the documentation, _eventStream is private. Is there a workaroud for this if no means are available at the moment?
NOTE: One could argue to look for BlocA state changes (ie BlocA.stream) and check whether correct states are emitted in response to FromBToAEvent. However, I need to write a unit test for BlocB without depending on the correctness of BlocA.
I am using bloc library available in Dart to implement "bloc" pattern. I will eventually move onto flutter_bloc library so I can use it inside a real app.
I'm having a bit of difficulty understanding how to create some general blocs that can be called from within more specialized blocs. By specialized I mean some bloc that perhaps manages a specific view. General blocs would then take care of calling APIs or maybe even doing multiple things sequentially.
So my idea is that perhaps I have a StateA that manages certain model and for that reason I use BlocA. Whenever certain event is added to BlocA, I also need to update StateB that is managed by BlocB. I do not want to do this within same bloc because those different states contain different data that might be unrelated. Perhaps I can then have BlocC that is used for specific part of application but certain event should also invoke events and state changes in BlocA and BlocB.
I am considering writing BlocA bloc like this:
class BlocA extends BlocBase<BlocAEvent, BlocAState> {
BlocA(BlocB blocB) : super(BlocAState()) {
_blocB = blocB;
_blocARepository = BlocARepository();
};
BlocARepository _blocARepository;
#override
BlocAState mapEventToState(BlocAEvent event) async* {
if (event is BlocAEventOne) {
yield state.copyWith(valueOne: event.value);
} else if (event is BlocAEventTwo {
// Get data related to BlocAState
final data = await _blocARepository.fetchImportantData()
// ! <-- I also need to fetch additional data but that is managed
// by different bloc - BlocB
_blocB.add(BlocBEventOne(id: data.id));
yield state.copyWith(dataList: SomeModel.fromJSON(data));
}
}
}
and then just creating regular bloc BlocB like this:
class BlocB extends BlocBase<BlocBEvent, BlocBState> {
BlocB() : super(BlocBState()) {
_blocBRepository = BlocBRepository();
};
BlocBRepository _blocBRepository;
#override
BlocBState mapEventToState(BlocBEvent event) async* {
if (event is BlocBEventOne) {
// Get data related to BlocBState
final data = await _blocBRepository.fetchOtherData(event.id)
yield state.copyWith(dataList: SomeOtherModel.fromJSON(data));
}
}
}
I am really unsure if this is correct approach as basically BlocA adds another event. But I am trying to do some reusable blocs and keep data more separate. I know I could also do _blocB.stream.listen in BlocA but that only gives me the state of the BlocB bloc. What I need is the ability to react to certain events.
Do you think that my approach is perhaps convoluted and using BlocObserver would perhaps be more appropriate?
The problem with BlocObserver is that I am unsure how to use it properly from within Flutter app.
First of all, what you are trying to achieve is completely fine, but let's talk about the architecture a little bit.
One option could be that BLoC B subscribes to BLoC A state changes and handles that accordingly. For instance, here a more specific BLoC subscribes to changes of a more generic one: https://github.com/felangel/bloc/blob/08200a6a03e37ce179cef10b65f34ddf6f43f936/examples/flutter_todos/lib/blocs/filtered_todos/filtered_todos_bloc.dart. For that, you would need to adjust the BLoC A state a bit (so you could identify this specific change/event) which could be not a way to go.
Another thing you should consider is whether you want to tightly couple your BLoCs (BLoC B reference is passed to BLoC A via constructor in your example). For that, you can create some kind of mediator class that uses streams inside: BLoC A could publish an event to that mediator class sink and all the subscribers will receive it via exposed streams (in your case - BLoC B should subscribe to that stream) - so the communication there would be like BLoC A -> Mediator -> BLoC B. That's basically what Mediator/Observer OOP design pattern resolves. The advantage of it is that your BLoCs won't know anything about each other, multiple BLoCs could subscribe to multiple different event streams, meaning you could add more subscribers (BLoCs) to your events at any point.
You can define a callback in your event and invoke it in your bloc like this:
blocA.add(BlocAEvent(whenDone: () {
blocB.add(BlocBEvent())
}));
Another way to do this is to listen to events being published by a BloC:
// Inside some widget
this.context.read<MyBloc>().listen((myBlocState) {
if (mounted && myBlocState.isSomeState) {
this.context.read<MyOtherBloc>().add(MyOtherBlocEvent());
}
});
This solution would ensure your BloCs are not coupled and you can chain the publishing of your events based on the state of another BloC.
Here is my architecture, I have two statfull widgets: ProfilePicture, RoundedProfilePicture. ProfilePicture is a statefull widget and it has complicated custom code which is very prone to error so I dont want to copy paste it, it utilizes variables from Picture abstract class. (ie. fetches server data and stores them inside variables of Picture). What I want to do is I want to extend from this widgets state so that I can create a new widget RoundedProfilePicture. This widgets state will basically utilize the complicated code from ProfilePicture's state with inheritance and it will add small extra logic to it. So I thought inheritance is the best choice here. Here is what I tried so far
class ProfilePictureState extends State<ProfilePicture> implements Picture{
// SOME LONG COMPLICATED 500 LINE OF CODE
}
class RoundedProfilePictureState extends ProfilePictureState {
#override
void initState() {
super.initState(); // this calls ProfilePictureState.initState() call. I want to call State<ProfilePicture>.initState()
}
}
My problem is void initState() in RoundedProfilePictureState requires me to make a super.initState() call. This call makes a ProfilePictureState.initState() call. I want to call State<ProfilePicture>.initState() because I want to add a different logic in my init state call. So the structure is:
----abstract State class
---ProfilePictureState
--RoundedProfilePictureState
How can I make a call to abstract State class's initState method from RoundedProfilePictureState? Is this possible?
Since no one answered, here is how I solved this. I think this is impossible to achieve. What I did is I moved the code and all the variables to a mixin. Took like an hour or two to do so. I achieved my needs tho.
I'm trying to make a authentication screen with the BLoC pattern, but I have doubts about best practices even after reading the documentation of the flutter_bloc lib and reading multiple implementations.
The main problem I have is about state management. My authentication screen should handle the following states:
Password visible or not.
Show register or login screen.
TextEdit validation.
Loading (waiting for a Firebase response).
if authenticated, show home. Else show the authentication page
I really wanted to do all the logic in blocs to make a clean architecture.
Some people say they use a bloc per screen, sometime a bloc for the whole app, but flutter_bloc library says you should use a bloc for any widget complex enough.
My problem is, how can I deal with multiple states?
If I store variables in states classes extended from the mother AuthState class, I can't access it in mapEventToState
because the block receives the mother just the AuthState class.
I tried handling all states in the same bloc by passing the current state in a event (as I will show in the code below), but then I can't properly set the initial state.
What's the best practice solution?
Passing all state classes as variables in the mother AuthState class?
Then I could persist data using "state.passwordFieldState". But I
never saw something like that. I bet it's a wrong approach.
Create a model to store the state and manipulate the model when a change event enter the bloc?
Creating multiple blocs or cubits? One cubit for authentication, 1 cubit for password visibility, one bloc for authentication handling? My concern with that would be nesting bloc builders.
Or should I forget about blocs for simple things and use providers instead? I wouldn't like to do that because it can lead to spaghetti code.
Here's my code:
class AuthState extends Equatable{
#override
List<Object> get props => [];
}
class PasswordFieldState extends AuthState{
final bool isObscured;
PasswordFieldState({this.isObscured});
#override
List<Object> get props => [isObscured];
}
class AuthEvent extends Equatable{
#override
List<Object> get props => [];
}
class SetObscurePassword extends AuthEvent{
bool isObscured = false;
}
class passwordTextEditChanged extends AuthEvent{}
class emailTextEditChanged extends AuthEvent{}
class AuthBloc extends Bloc<AuthEvent,AuthState> {
AuthBloc(AuthState initialState) : super(initialState);
#override
Stream<AuthState> mapEventToState(AuthEvent event) async* {
if (event is SetObscurePassword) {
yield PasswordFieldState(isObscured: !event.isObscured);
} else if (event is passwordTextEditChanged) {
print("validation handling");
} else if (event is emailTextEditChanged) {
print("validation handling");
}
}
I think I found the answer.
It's not possible to make a bloc for the entire application using the flutter_bloc library. It's only possible to do such thing using stream controllers to create a bloc without the library.
But it's not a bad thing to create a bloc for each different task. It makes the code more testable and easier to understand.