Search code examples
flutterbloc

Navigator with bloc listener


Suppose I have Form in screen A with save button that run post action in cubit:

Future<void> postAssignment(List<Assigment?> assignments) async {
    emit(state.copyWith(status: AssignmentStatus.posting));

    try {
      await _warehouseRepository.postAssignments(
          assignments.map((a) => AssigmentApi.Assigment.toApi(a!)).toList());

      emit(state.copyWith(status: AssignmentStatus.success));
    } on Exception catch (e) {
      print("Error message: ${e.toString()}");
      emit(state.copyWith(status: AssignmentStatus.failure));
    }
  }

After this I want to stay in screen A with reseting state and I want to show snackBar that item is posted. So I have BlocListener for it:

BlocListener<AssignmentCubit, AssignmentState>(
                listener: (context, state) async {
                  print('AssignmentCubit-------------->: $state');
                  if (state.status.isPosting) {
                    AppKeys.NAVIGATOR_KEY.currentState?.pushNamed(
                      SplashPage.routeName,
                    );
                  }
                  if (state.status.isSuccess) {
                    await context.read<ViewItemCubit>().fetchViewItems(
                          int.parse(
                            context.read<ItemBarcodeCubit>().state,
                          ),
                        );
                    final snackBar = SnackBar(
                      duration: Duration(seconds: 2),
                      content: Text('added'),
                      backgroundColor: Colors.green,
                    );
                    ScaffoldMessenger.of(context).showSnackBar(snackBar);
                  } else {
                    AppKeys.NAVIGATOR_KEY.currentState?.popUntil(
                      (route) =>
                          route.settings.name == ItemLocatePage.routeName,
                    );
                  }
                },
              ),

but I think the state isn't reset and it's stacking, this is the result if I post first item:

I/flutter (30679): AssignmentCubit-------------->: AssignmentState(AssignmentStatus.posting, [])
I/flutter (30679): AssignmentCubit-------------->: AssignmentState(AssignmentStatus.success, [])

second item:

I/flutter (30679): AssignmentCubit-------------->: AssignmentState(AssignmentStatus.posting, [])
I/flutter (30679): AssignmentCubit-------------->: AssignmentState(AssignmentStatus.posting, [])
I/flutter (30679): AssignmentCubit-------------->: AssignmentState(AssignmentStatus.success, [])
I/flutter (30679): AssignmentCubit-------------->: AssignmentState(AssignmentStatus.success, [])

and so on:

I/flutter (30679): AssignmentCubit-------------->: AssignmentState(AssignmentStatus.posting, [])
I/flutter (30679): AssignmentCubit-------------->: AssignmentState(AssignmentStatus.posting, [])
I/flutter (30679): AssignmentCubit-------------->: AssignmentState(AssignmentStatus.posting, [])
I/flutter (30679): AssignmentCubit-------------->: AssignmentState(AssignmentStatus.success, [])
I/flutter (30679): AssignmentCubit-------------->: AssignmentState(AssignmentStatus.success, [])
I/flutter (30679): AssignmentCubit-------------->: AssignmentState(AssignmentStatus.success, [])

thanks to this the snackBar appears 3 times. Generally, I think the problem is in Navigator, because If I posted item the state isn't reset (maybe screen isn't dispose?). What is wrong?

Edit: build method:

@override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        AppKeys.NAVIGATOR_KEY.currentState?.pushNamedAndRemoveUntil(
          HomePage.routeName,
          (route) => false,
        );
        return Future.value(true);
      },
      child: BlocBuilder<ViewItemFormCubit, ViewItem?>(
        builder: (context, state) {
          final dropdownValue =
              context.read<ViewItemFormCubit>().state == ViewItem.empty()
                  ? null
                  : context.read<ViewItemFormCubit>().state;
          return MultiBlocListener(
            listeners: [
              BlocListener<ViewItemCubit, ViewItemState>(
                listener: (context, state) {
                  if (state.status.isLoading) {
                    AppKeys.NAVIGATOR_KEY.currentState?.pushNamed(
                      SplashPage.routeName,
                    );
                  } else {
                    AppKeys.NAVIGATOR_KEY.currentState?.popUntil(
                      (route) =>
                          route.settings.name == ItemLocatePage.routeName,
                    );
                  }
                },
              ),
              BlocListener<AssignmentCubit, AssignmentState>(
                listener: (context, state) async {
                  print('AssignmentCubit-------------->: $state');
                  if (state.status.isPosting) {
                    AppKeys.NAVIGATOR_KEY.currentState?.pushNamed(
                      SplashPage.routeName,
                    );
                  }
                  if (state.status.isSuccess) {
                    await context.read<ViewItemCubit>().fetchViewItems(
                          int.parse(
                            context.read<ItemBarcodeCubit>().state,
                          ),
                        );
                    final snackBar = SnackBar(
                      duration: Duration(seconds: 2),
                      content: Text('added'),
                      backgroundColor: Colors.green,
                    );
                    ScaffoldMessenger.of(context).showSnackBar(snackBar);
                  } else {
                    AppKeys.NAVIGATOR_KEY.currentState?.popUntil(
                      (route) =>
                          route.settings.name == ItemLocatePage.routeName,
                    );
                  }
                },
              ),
            ],
            child: Scaffold(
              appBar: AppBar(
                leading: IconButton(
                  icon: Icon(Icons.arrow_back),
                  onPressed: () {
                    _clearAllItemsInList();
                    AppKeys.NAVIGATOR_KEY.currentState?.pushNamedAndRemoveUntil(
                        HomePage.routeName, (route) => false);
                  },
                ),
                title: Text(
                    'Locate item ${context.read<ItemBarcodeCubit>().state}'),
                centerTitle: true,
              ),
              floatingActionButton: Padding(
                padding: const EdgeInsets.only(bottom: 40, right: 20),
                child: FloatingActionButton(
                  onPressed: () => scanBarcodeNormal(dropdownValue),
                  child: Icon(
                    const IconData(0xefe1, fontFamily: 'MaterialIcons'),
                    size: 35.0,
                  ),
                  backgroundColor: Color(0xff33691e),
                  foregroundColor: Colors.white,
                ),
              ),
              body: Center(
                child: Form(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.start,
                    children: [
                      SizedBox(
                        height: 20,
                      ),
                      Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: DropdownButtonFormField<ViewItem>(
                          decoration: InputDecoration(
                            filled: true,
                            labelText: 'Item',
                          ),
                          value: dropdownValue,
                          icon: const Icon(Icons.arrow_downward),
                          elevation: 16,
                          items: context
                              .read<ViewItemCubit>()
                              .state
                              .viewItems
                              ?.map<DropdownMenuItem<ViewItem>>(
                            (item) {
                              return DropdownMenuItem<ViewItem>(
                                value: item,
                                child: Text(
                                  '${item.itemId}. ${item.materialNumber} ${item.materialDescription}',
                                ),
                              );
                            },
                          ).toList(),
                          onChanged: (ViewItem? newValue) {
                            _clearAllItemsInList();
                            context.read<ViewItemFormCubit>().set(newValue!);
                          },
                        ),
                      ),
                      BlocBuilder<AssigmentItemsCubit, AssigmentItemsState>(
                        builder: (context, state) {
                          return Expanded(
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.start,
                              children: [
                                Container(
                                  decoration: BoxDecoration(
                                    color: Colors.white,
                                    borderRadius:
                                        BorderRadius.all(Radius.circular(5)),
                                    boxShadow: [
                                      BoxShadow(
                                        color: Colors.grey.withOpacity(0.2),
                                        spreadRadius: 5,
                                        blurRadius: 15,
                                        offset: Offset(0, 10),
                                      ),
                                    ],
                                  ),
                                  child: dropdownValue != null
                                      ? Text(
                                          'Left to assign: ${dropdownValue.totalQuantity - dropdownValue.assignedQuantity - context.read<AssigmentItemsCubit>().state.items.fold(
                                                0,
                                                (acc, item) {
                                                  return acc + item!.quantity;
                                                },
                                              )}',
                                          style: TextStyle(fontSize: 20),
                                        )
                                      : Text(''),
                                ),
                                AssigmentItems(
                                  locationListKey: locate_list_key,
                                ),
                              ],
                            ),
                          );
                        },
                      ),
                      ElevatedButton(
                        onPressed: () {
                          final assignments =
                              context.read<AssigmentItemsCubit>().state.items;
                          context
                              .read<AssignmentCubit>()
                              .postAssignment(assignments);
                          _clearAllItemsInList();
                        },
                        child: Text('Save'),
                      )
                    ],
                  ),
                ),
              ),
            ),
          );
        },
      ),
    );

Solution

  • I solved this by code:

    BlocListener<AssignmentCubit, AssignmentState>(
                listener: (context, state) async {
                  print('AssignmentCubit-------------->: $state');
                  if (state.status.isPosting) {
                    AppKeys.NAVIGATOR_KEY.currentState?.pushReplacementNamed(
                      SplashPage.routeName,
                    );
                  }
                  if (state.status.isSuccess) {
                    await context.read<ViewItemCubit>().fetchViewItems(
                          int.parse(
                            context.read<ItemBarcodeCubit>().state,
                          ),
                        );
                    final snackBar = SnackBar(
                      duration: Duration(seconds: 2),
                      content: Text('added'),
                      backgroundColor: Colors.green,
                    );
                    ScaffoldMessenger.of(context).showSnackBar(snackBar);
                  }
                  if (state.status.isInitial) {
                    AppKeys.NAVIGATOR_KEY.currentState?.pushNamed(
                      ItemLocatePage.routeName,
                    );
                  }
                },
              ),
    

    FYI, look out what you have in your Navigator stack, in my example I had previous screen with BlocListener and from here I pushed screen A but previous screen (with BlocListener) didn't dispose so it also listened changes in my screen A. It was hard to diagnose what was going on.