Showing a dialog with BlocListener

Issue

I have 2 textFields that have a gesture detector and on tap of the gesture detector, I open an AlertDialog using the BlocListener widget.

Widget _getSourceAndOriginTextField(String hintText, controller) {
    final portsCubit = BlocProvider.of<PortsCubit>(context);
    return TextFormField(
      controller: controller..text = "",
      readOnly: true,
      decoration: InputDecoration(
          suffixIcon: GestureDetector(
              onTap: () {
                portsCubit.getAllPorts();
              },
              child: BlocListener<PortsCubit, PortsState>(
                listenWhen: (previous, current) => previous != current,
                listener: (context, state) async {
                  if (state is PortsLoadedState) {
                    await showDialog(
                        context: context,
                        builder: (_) => BlocProvider.value(
                              value: portsCubit,
                              child: PortsDialog(state.portsList),
                            ));
                  }
                },
                child: SvgPicture.asset(
                  'assets/svg/chevron_down_icon.svg',
                  fit: BoxFit.scaleDown,
                  height: 10,
                  width: 18,
                ),
              )),
          focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(8.0),
            borderSide: BorderSide(
              color: Colors.transparent,
              style: BorderStyle.solid,
              width: 1,
            ),
          ),
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(8.0),
            borderSide: BorderSide.none,
          ),
          filled: true,
          contentPadding: EdgeInsets.all(8),
          hintText: hintText,
          hintStyle: AppStyle.hintStyle),
    );
  }

This method above which returns a TextField is being used inside the form to display two textfields.

Form(
    children:[_getSourceAndOriginTextField('Origin',controller),
    SizedBox(height:10),
    _getSourceAndOriginTextField('Origin',controller)])

When I tap on one of the textfields the state changes and the dialog opens but the dialog is displayed twice. What am I missing here?

Following are the images for reference, any help will be appreciated.

TextField inside the form.

The AlertDialog

Solution

I have 2 textFields

and

This method above which returns a TextField is being used inside the form to display two textfields.

basically explains your problem. Since you are executing _getSourceAndOriginTextField() twice, you also create the BlocListener<PortsCubit, PortsState>(...) twice. Thus, when the PortsCubit state changes to PortsLoadedState, both listeners trigger the showDialog function.

What you can do is lift the BlocListener and wrap the Form with it. This way you will have only a single listener that could open the dialog:

BlocListener<PortsCubit, PortsState>(
  listenWhen: (previous, current) => previous != current,
  listener: (context, state) async {
    if (state is PortsLoadedState) {
      await showDialog(
        context: context,
        builder: (_) => BlocProvider.value(
          value: portsCubit,
          child: PortsDialog(state.portsList),
      ));
  },
  child: Form(
    children:[_getSourceAndOriginTextField('Origin',controller),
    SizedBox(height:10),
    _getSourceAndOriginTextField('Origin',controller)]) 
},

Answered By – mkobuolys

Answer Checked By – Candace Johnson (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *