How to access Flutter BLoC state value in the new version?

Viewed 1306

I am new in this state management world. So I was trying to follow this tutorial (How to Save Products in a Wishlist using the BloC Pattern - EP10 - The eCommerce Series), but mapEventToState is deprecated so I am not sure what to do.

Here is my state:

part of 'wishlist_bloc.dart';

abstract class WishlistState extends Equatable {
  const WishlistState();

  @override
  List<Object> get props => [];
}

class WishlistLoading extends WishlistState {}

class WishlistLoaded extends WishlistState {
  final WishlistModel wishlist;

  const WishlistLoaded({this.wishlist = const WishlistModel()});

  @override
  List<Object> get props => [wishlist];
}

class WishlistError extends WishlistState {}

Here is my event:

part of 'wishlist_bloc.dart';

abstract class WishlistEvent extends Equatable {
  const WishlistEvent();

  @override
  List<Object> get props => [];
}

class StartWishlist extends WishlistEvent {}

class AddWishlistProduct extends WishlistEvent {
  final ProductModel product;

  const AddWishlistProduct(this.product);

  @override
  List<Object> get props => [product];
}

class RemoveWishlistProduct extends WishlistEvent {
  final ProductModel product;

  const RemoveWishlistProduct(this.product);

  @override
  List<Object> get props => [product];
}

Here is my bloc:

import '../models/product_model.dart';
import '../models/wishlist_model.dart';

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';

part 'wishlist_event.dart';
part 'wishlist_state.dart';

class WishlistBloc extends Bloc<WishlistEvent, WishlistState> {
  WishlistBloc() : super(WishlistLoading()) {
    on<StartWishlist>(_mapStartWishlistToState);
    on<AddWishlistProduct>(_mapAddWishlistToState);
    on<RemoveWishlistProduct>(_mapRemoveWishlistToState);
  }

  void _mapStartWishlistToState(event, emit) async {
    emit(WishlistLoading());

    try {
      await Future.delayed(Duration(seconds: 1));
      emit(WishlistLoaded());
    } catch (_) {}
  }

  // Error ...

  void _mapAddWishlistToState(event, emit) async {
    if (state is WishlistLoaded) {
      try {
        emit(WishlistLoaded(
            wishlist: WishlistModel(
                products: List.from(state.wishlist.products)
                  ..add(event.product))));
      } catch (_) {}
    }
  }

  void _mapRemoveWishlistToState(event, emit) async {}
}

But I get this error instead: "The getter 'wishlist' isn't defined for the type 'WishlistState'. Try importing the library that defines 'wishlist', correcting the name to the name of an existing getter, or defining a getter or field name 'wishlist'".

How to access 'wishlist' in the new version of flutter_bloc? Thank you.

3 Answers

Try to define arguments types while difining the functions. So your updated code would look like this:

import '../models/product_model.dart';
import '../models/wishlist_model.dart';

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';

part 'wishlist_event.dart';
part 'wishlist_state.dart';

class WishlistBloc extends Bloc<WishlistEvent, WishlistState> {
  WishlistBloc() : super(WishlistLoading()) {
    on<StartWishlist>(_mapStartWishlistToState);
    on<AddWishlistProduct>(_mapAddWishlistToState);
    on<RemoveWishlistProduct>(_mapRemoveWishlistToState);
  }

  void _mapStartWishlistToState(
     // Added argument types below
     StartWishlist event, Emitter<WishlistState> emit) async {
    emit(WishlistLoading());

    try {
      await Future.delayed(Duration(seconds: 1));
      emit(WishlistLoaded());
    } catch (_) {}
  }

  void _mapAddWishlistToState(
     // Added argument types below
     AddWishlistProduct event, Emitter<WishlistState> emit) async {
    if (state is WishlistLoaded) {
      try {
        emit(WishlistLoaded(
            wishlist: WishlistModel(
                products: List.from(state.wishlist.products)
                  ..add(event.product))));
      } catch (_) {}
    }
  }

  void _mapRemoveWishlistToState(
     // Added argument types below
      RemoveWishlistProduct event, Emitter<WishlistState> emit) async {}
}

Type promotion is impossible for properties, since they can potentially return different values each time they are called. As such, it is impossible for the compiler to know that the state getter will return a WishlistLoaded instance, even after knowing that the same getter returned a WishlistLoaded four lines earlier.

One way around this is to assign the state to a local variable, which is eligible for type promotion.

  void _mapAddWishlistToState(AddWishlistProduct event, Emitter<WishlistState> emit) async {
    final state = this.state; // local variable
    if (state is WishlistLoaded) {
      try {
        emit(WishlistLoaded(
            wishlist: WishlistModel(
                products: List.from(state.wishlist.products)
                  ..add(event.product))));
      } catch (_) {}
    }
  }

The linked video used a parameter, which is also eligible for type promotion. If it had used the state getter directly in _mapAddWishlistProductToState, it would have run into the same error.

You just need to cast your state as following

void _mapAddWishlistToState(event, emit) async {
    if (state is WishlistLoaded) {
      try {
        emit(WishlistLoaded(
            wishlist: WishlistModel(
                products: List.from((state as WishlistLoaded).wishlist.products)
                  ..add(event.product))));
      } catch (_) {}
    }
  }
Related