简体   繁体   中英

Cubit not rebuilding when emiting a new state

Whenever I call the toggleLocked event, the BlocBuilder does not rebuild the widget.

I have looked around a lot on the internet and found this explanation: https://stackoverflow.com/a/60869187/3290471 I think that somewhere I incorrectly use the equatable package which results in the fact that the BlocBuilder thinks nothing has changed (while is has).

I have read the FAQ from the Bloc libray and the three provided solution (props for equatable / not reusing the same state / using fromList) seem to not fix the problem.

My Cubit:

class LockCubit extends Cubit<LockState> {
  LockCubit({@required this.repository})
      : assert(repository != null),
        super(LockInitial());

  final LocksRepository repository;

  Future<void> fetch() async {
    try {
      final locks = await repository.fetchLocks();
      emit(LocksDisplayed().copyWith(locks));
    } on Exception {
      emit(LockError());
    }
  }

  Future<void> toggleLocked(int id) async {
    try {
      final locks = await repository.toggleLocked(id);
      emit(LocksDisplayed().copyWith(List.from(locks)));
    } on Exception {
      emit(LockError());
    }
  }
}

My states:

abstract class LockState extends Equatable {
  const LockState();

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

class LockInitial extends LockState {
  @override
  String toString() => 'LocksUninitialized';
}

class LockError extends LockState {
  @override
  String toString() => 'LockError';
}

class LocksDisplayed extends LockState {
  final List<Lock> locks;

  const LocksDisplayed([this.locks = const []]);

  LocksDisplayed copyWith(locks) => LocksDisplayed(locks ?? this.locks);

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

  @override
  String toString() => 'LocksDisplayed { locks: $locks }';
}

My model:

class Lock extends Equatable {
  Lock({this.id, this.name, this.locked, this.displayed});

  final int id;
  final String name;
  final bool locked;
  final bool displayed;

  @override
  String toString() =>
      'Lock { id: $id name: $name locked: $locked displayed: $displayed }';

  Lock copyWith({id, name, locked, displayed}) => Lock(
      id: id ?? this.id,
      name: name ?? this.name,
      locked: locked ?? this.locked,
      displayed: displayed ?? this.displayed);

  @override
  List<Object> get props => [id, name, locked, displayed];
}

My repositotory:

class LocksRepository {
  List<Lock> locks = [];

  Future<List<Lock>> fetchLocks() async {
    // This is a temporary implementation
    // In the future the data should be fetched from a provider

    locks = [
      new Lock(
        id: 0,
        name: 'Voordeur',
        locked: false,
      ),
      new Lock(
        id: 1,
        name: 'Achterdeur',
        locked: false,
      )
    ];

    return locks;
  }

  Future<List<Lock>> toggleLocked(int id) async {
    // This is a temporary implementation
    // In the future a request to change a lock should be made and then the specific lock should be retrieved back and edited.

    locks[id] = locks[id].copyWith(locked: !locks[id].locked);

    return locks;
  }
}

I am changing a state with the following trigger:

context.read<LockCubit>().toggleLocked(focusedIndex);

I am using BlocBuilder like this to build the state:

BlocBuilder<LockCubit, LockState>(builder: (context, state) {
      print('State Changed');

      if (state is LockInitial) {
        return Text('lockInitial');
      }

      if (state is LocksDisplayed) {
        return Swiper(
            itemBuilder: (BuildContext context, int index) {
              return Column(
                children: [
                  Text(state.locks[index].name),
                  Text(state.locks[index].locked.toString())
                ],
              );
            },
            onIndexChanged: onIndexChanged,
            loop: true,
            itemCount: state.locks.length);
      }

      if (state is LockError) {
        return Text('lockError');
      }

      return Container();
    });

All help would be very appreciated.

Can you check BlocProvider? I got the same problem. If this bloc inside materialApp, you must pass BlocProvider.value not create in widget.

I am a bit confused, if this could work. But with a bloc you would use an event not a cubit (even though events are based on cubits).

So first of all I would use the standard pattern: state event bloc with mapEventToState

Then, what I also do not see in your code, if you toggle your lock it would look like this in pseudo code

if (event is toggleLock) {
  yield lockInProgress();
  toggleLock();
  yield locksDisplayed;
}

This way your state always changes from locksDisplayed to lockInProgress to locksDisplayed - just as you read in your link above

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM