felangel / bloc

A predictable state management library that helps implement the BLoC design pattern
https://bloclibrary.dev
MIT License
11.82k stars 3.39k forks source link

[Question]: BlocListener not working for one cubit instance #3260

Closed eRuaro closed 2 years ago

eRuaro commented 2 years ago

Hey! I'm trying to do navigation using BlocListener I have two cubits, one works wherein BlocListener navigates to a new screen upon successful state change, while the other does not.

Here's the code for the cubit that works:

class WalletCreateDialog extends StatefulWidget {
  const WalletCreateDialog({required this.mnemonic});

  final String mnemonic;

  @override
  _WalletCreateDialogState createState() => _WalletCreateDialogState();
}

class _WalletCreateDialogState extends State<WalletCreateDialog> {
  @override
  void initState() {
    BlocProvider.of<WalletCreateCubit>(context)
        .addCreatedWalletToWalletList(widget.mnemonic);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return BlocListener<WalletCreateCubit, WalletCreateState>(
      listener: (context, state) {
        if (state is WalletAdded) {
          debugPrint('creation listener called');
          showDialog(
            context: context,
            barrierDismissible: false,
            builder: (context) => AlertDialog(
              content: Text(
                'Wallet added! Navigating back to home screen...',
              ),
            ),
          );
          Navigator.of(context).pushNamedAndRemoveUntil(
            WalletOverviewHomeScreen.routeName,
            (route) => false,
          );
        }
      },
      child: AlertDialog(
        content: Container(
          height: MediaQuery.of(context).size.height * 0.08,
          child: Row(
            children: [
              Expanded(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    Text("Adding wallet..."),
                    const LoadingIndicator(),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
class WalletCreateCubit extends Cubit<WalletCreateState> {
  WalletCreateCubit() : super(WalletCreateInitial());

  void createWallet() {
    emit(WalletCreateLoading());
    String mnemonic = bip39.generateMnemonic();
    if (bip39.validateMnemonic(mnemonic)) {
      debugPrint('Generated and validated mnemonic');
      emit(WalletCreated(mnemonic: mnemonic));
    }
  }

  Future<void> addCreatedWalletToWalletList(String mnemonic) async {
    FlutterSecureStorage storage = FlutterSecureStorage();
    emit(WalletAdding());
    alan.Wallet wallet = await compute(
      deriveWallet,
      mnemonic,
    );
    addToWallet(
      storage,
      mnemonic,
      wallet.bech32Address,
    );
    emit(WalletAdded());
  }

  void emitWalletCreateInitial() {
    emit(WalletCreateInitial());
  }
}
part of 'wallet_create_cubit.dart';

abstract class WalletCreateState extends Equatable {
  const WalletCreateState();

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

class WalletCreateInitial extends WalletCreateState {
  const WalletCreateInitial();

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

class WalletCreateLoading extends WalletCreateState {
  const WalletCreateLoading();

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

class WalletCreated extends WalletCreateState {
  final String mnemonic;

  const WalletCreated({required this.mnemonic});

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

class WalletAdding extends WalletCreateState {
  const WalletAdding();

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

class WalletAdded extends WalletCreateState {
  const WalletAdded();

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

While here's the code for the cubit that does not navigate to a different screen upon successful state change

class WalletDeleteDialog extends StatefulWidget {
  const WalletDeleteDialog({required this.walletAddress});

  final String walletAddress;

  @override
  State<WalletDeleteDialog> createState() => _WalletDeleteDialogState();
}

class _WalletDeleteDialogState extends State<WalletDeleteDialog> {
  @override
  void initState() {
    BlocProvider.of<WalletDeleteCubit>(context)
        .deleteWallet(widget.walletAddress);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return BlocListener<WalletDeleteCubit, WalletDeleteState>(
      listener: (context, state) {
        if (state is WalletDeleteFinished) {
          debugPrint('delete listener called');
          showDialog(
            context: context,
            barrierDismissible: false,
            builder: (context) => AlertDialog(
              content: Text(
                'Wallet deleted! Navigating back to home screen...',
              ),
            ),
          );
          Navigator.of(context).pushNamedAndRemoveUntil(
            WalletOverviewHomeScreen.routeName,
            (route) => false,
          );
        }
      },
      child: AlertDialog(
        content: Container(
          height: MediaQuery.of(context).size.height * 0.08,
          child: Row(
            children: [
              Expanded(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    Text("Deleting wallet..."),
                    const LoadingIndicator(),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
class WalletDeleteCubit extends Cubit<WalletDeleteState> {
  WalletDeleteCubit() : super(WalletDeleteInitial());

  void emitWalletDeleteInitial() {
    emit(WalletDeleteInitial());
  }

  Future<void> deleteWallet(String address) async {
    FlutterSecureStorage storage = FlutterSecureStorage();
    emit(WalletDeleteOngoing());
    deleteFromWallet(storage, address);
    debugPrint("Wallet with address: $address is deleted");
    emit(WalletDeleteFinished());
    debugPrint('Emit WalletDeleteFinished');
  }
}
part of 'wallet_delete_cubit.dart';

abstract class WalletDeleteState extends Equatable {
  const WalletDeleteState();

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

class WalletDeleteInitial extends WalletDeleteState {
  const WalletDeleteInitial();

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

class WalletDeleteOngoing extends WalletDeleteState {
  const WalletDeleteOngoing();

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

class WalletDeleteFinished extends WalletDeleteState {
  const WalletDeleteFinished();

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

I've already verified that the state changes for WalletDeleteState but for some reason it still doesn't navigate to a different screen, while the other cubit, WalletCreateCubit does. That is, the BlocListener isn't being called. How can I fix this?

eRuaro commented 2 years ago

Looks like the fix was to directly copy the contents of deleteFromWallet to the deleteWallet function.

void deleteWallet(String address) async {
    FlutterSecureStorage storage = FlutterSecureStorage();
    emit(WalletDeleteOngoing());
    await storage.delete(
      key: WalletOverviewHomeScreen.walletKey + address,
    );
    debugPrint("Wallet with address: $address is deleted");
    emit(WalletDeleteFinished());
    debugPrint('Emit WalletDeleteFinished');
  }