6

I get a _CastError error at the last line of this piece of code

BlocBuilder buildUsernameField() {
  return BlocBuilder<ProfileBloc, ProfileState>(
    buildWhen: (previous, current) => previous != current && current is EditingUserInfo,
    builder: (context, state) => TextField(
      keyboardType: TextInputType.name,
      controller: TextEditingController(
          text: (state as EditingUserInfo).username.value),

saying that

I/flutter (26787): The following _CastError was thrown building BlocBuilder<ProfileBloc, ProfileState>(dirty, state:
I/flutter (26787): _BlocBuilderBaseState<ProfileBloc, ProfileState>#25b87):
I/flutter (26787): type 'Success' is not a subtype of type 'EditingUserInfo' in type cast

So what's happening is that it tries to build that widget when I am in another state (success). But in the buildWhen parameter, I specified that the widget should only build when the state is EditingUserInfo.

So as far as I understand, this error should not happen.

Here's my ProfileState :

part of 'profile_bloc.dart';

abstract class ProfileState extends Equatable {
  const ProfileState();
  
  @override
  List<Object> get props => [];
}

class ProfileInitial extends ProfileState {}

class EditingUserInfo extends ProfileState {
  final Username username;
  final Bio bio;
  final PhotoUrl photoUrl;
  final City city;
  final FormzStatus status;

  const EditingUserInfo({
    this.username = const Username.pure(),
    this.bio = const Bio.pure(),
    this.photoUrl = const PhotoUrl.pure(),
    this.city = const City.pure(),
    this.status = FormzStatus.pure,
  });

  EditingUserInfo copyWith({Username username, Bio bio, PhotoUrl photoUrl, City city, FormzStatus status}){
    return EditingUserInfo(
      username: username ?? this.username,
      bio: bio ?? this.bio,
      photoUrl: photoUrl ?? this.photoUrl,
      city: city ?? this.city,
      status: status ?? this.status,
    );
  }

  @override
  List<Object> get props => [username, bio, photoUrl, city];
}

class Loading extends ProfileState {}

class Error extends ProfileState {
  final String message;

  const Error({this.message});

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

class Success extends ProfileState {
  final String message;

  const Success({this.message});

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

user54517
  • 2,020
  • 5
  • 30
  • 47

2 Answers2

8

You still have to check that the state variable is the proper state. The state is checked EVERY time it is changed, so the state variable can still be a different state, it just doesn't rebuild unless the buildWhen conditions are true.

BlocBuilder buildUsernameField() {
return BlocBuilder<ProfileBloc, ProfileState>(
buildWhen: (previous, current) => previous != current && current is EditingUserInfo,
builder: (context, state) {
 if(state is EditingUserInfo) {
  return TextField(
  keyboardType: TextInputType.name,
  controller: TextEditingController(
      text: state.username.info)
}
}
Scott Godfrey
  • 641
  • 5
  • 8
  • Ok, si it means that I did not understand the `buildWhen` and `builder` fields quite well. So far, I thought that the widget returned in `builder` would build itself when and only when a boolean condition specified in `buildWhen` would be true. But what you're showing to me now is that what I thought is wrong. The widget should be returned not it the `builder` but in the `buildWhen` to be built only when the condition is true. Is that right ? – user54517 Feb 14 '21 at 09:15
  • 1
    Nope I was wrong. What you have is mostly right, but in builder method, you still have to check to make sure it is the proper state. The BlocBuilder still checks EVERY state change, which means the 'state' variable can still be a different state, it just doesn't rebuild unless it's in that state. I have edited my answer. – Scott Godfrey Feb 14 '21 at 14:20
  • 1
    I see, but what's the purpose of the `buildWhen` field ? Because the name suggests that the whole point of this property is to check when the builder function should run :O – user54517 Feb 14 '21 at 20:15
  • 1
    It is. Which means the builder will only rebuild the widget when your condition is true, but it still "checks" all of the state changes. Just make sure you're returning false on all other conditions. – Scott Godfrey Feb 16 '21 at 02:31
  • but the builder method need widget to be returned. which is not the case in ur answer; – user3066829 Jan 21 '22 at 16:44
  • This is an extremely late update, I fixed the answer. I missed the return before the text field. – Scott Godfrey Jan 30 '22 at 23:37
0

Here's a clear answer to your questioning user54517 :

buildWhen will prevent your widget to rebuild if the bloc state changes, but it will not prevent it if the framework rebuilds it for any other reason (for instance, if one of its parents need a rebuild...).

That's why you still need to check the state in the builder method.

Source: https://github.com/felangel/bloc/issues/1413#issuecomment-655568783

LW001
  • 2,452
  • 6
  • 27
  • 36
Nicolas
  • 114
  • 1
  • 7