Simple overview on using cubits for state management

Deployment Jul 31, 2022

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?

  1. We call the super of the class; with this we set the initial state
  2. 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
  3. A function to reset the state
  4. 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! šŸ˜

Tags

Ramiro

Hey hi! My name is Ramiro, this is my blog about artificial intelligence, coding and things that are in my head. Follow me on twitter @ramgendeploy so we can chat!

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.