Alright so I'm making a revamp of my AiStickerMaker app and I have a functionality where you select an image from the gallery or the camera and a SliverAppBar
widget will update with that image.
For this i was using a singleton to manage the state and passing that around, but as you can guess it wasn't a great option, it turns out changing the data in the singleton wasn't updating the state of the child widget.
So I decided to give blocs/cubits
a shot, a decided this instead of provider because I thought that in the long run will be a better option.
Let's first see the implementation of the cubit I used, for what I gathered a cubit is a simple form of a bloc as it extends BlocBase
, but it only holds one type of data, this can be a complex class with multiple fields or just a String
.
class ImageSelectedCubit extends Cubit<String?> {
// 1
ImageSelectedCubit() : super('');
// 2
void getNewImage(ImageSource fileSource) async {
final ImagePicker picker = ImagePicker();
final XFile? pickedFile = await picker.pickImage(source: fileSource);
emit(pickedFile?.path);
}
// 3
void resetImage() {
emit('');
}
// 4
@override
void onChange(Change<String?> change) {
super.onChange(change);
debugPrint(change.currentState);
debugPrint(change.nextState);
}
}
First we create the cubit class extending cubit and declaring that it holds a String?
- We call the super of the class; with this we set the initial state
- This one is the function that updates the state with the selected image, here we call the pickImage function and with the result we call emit to update the value of the cubit
- A function to reset the state
- Then with this we can debug the change of state.
Alright awesome right? we have a cubit now what?
Well we need to provide this bloc in the right places, and use a consumer to listen to the bloc.
MaterialApp( //...
home: MultiBlocProvider(providers: [
BlocProvider(
create: (context) => ImageSelectedCubit(),
),
], child: const HomePage()),
);
Now the best way I saw how to do this is to wrap the home in the MateralApp
widget with a MultiBlocProvider
, then we can state all of our providers inside of this widget and use them inside any children that follows this context.
Let's see how to actually use it and update a widget
For this we need to use the BlocConsumer
widget, as said before a cubit is just a simpler Bloc so we can use any Bloc widget with it.
BlocConsumer<ImageSelectedCubit, String?>(
listener: (context, state) {
final imgCubit = context.read<ImageSelectedCubit>();
if (state == null) {
showSnack('Error!', context);
imgCubit.resetImage();
}
},
builder: (context, state) {
final imgCubit = context.read<ImageSelectedCubit>();
return Image(image: FileImage(File(imgCubit.state!)));
})
Here we create a BlocConsumer
with the type of the cubit that we want and the type of data it holds here <ImageSelectedCubit, String?>
Then this is optional but we can do data validation before we build the widget, here we check if the image selected is null and we reset the value to an empty string.
Now in the builder function we can access the instance of the cubit using context.read<T>()
( T is the class of your cubit ) then reading .state we access the value of the cubit.
And that's it, I really liked the simplicity of cubits, but I'm sure I'll be diving deeper into Blocs for the rest of the app, if you want to follow the development check out de YT vid! 😁
Top comments (4)
I've no experience with cubits, but it feels to me like there is a lot of boilerplate for just storing the state of a string. Are cubits supposed to be small units of state or can they also hold more complex states?
I've been using provider on most apps we do at work, but I'm always interested in other solutions, but they always feel to bloated with boilerplate and stuff that makes me think again.
What benefits would you say cubits/bloc have over plain Providers?
Interesting post nonetheless, I will keep an eye for further updates on the subject!
You are right, but Cubit is the simplified version of Bloc. Imagine the Bloc boilerplate. Of course there are specific usecases for these state management solutions, for instance I would use Bloc/Cubit for larger and more complex projects, and I would use Riverpod for most projects. That is my take.
That's interesting! i didn't have riverpod on my radar, i think it wasn't released at the moment i was doing the project xD
Maybe in the next one i'll try it out.
Yea the boiler plate is something i also didn't quite like, but perhaps I'm using a sledge hammer to drive a nail here xD
I'm very new to the bloc library but for what i understood cubits are similar to Providers, i guess i choose bloc because it may be a little more versatile in the future.
That's one of the areas that i want to explore, but i think bloc's might be more robust, and be more suitable for more complex state systems.
I think for a first look and having used other state management in other frameworks cubits seem pretty alright, we'll as I continue using it, definitely gonna do a more in depth blog with Blocs.