BLoC: listen callback called multiple times

Issue

I have a Stateful Widget which obtains a bloc from its parent:

class Page extends StatefulWidget {
  @override
  State<StatefulWidget> createState() =>
  _PageState();
}

class _PageState extends State<Page> {
  final TextEditingController mnemonicController = TextEditingController();
  final _scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  Widget build(BuildContext context) {

final MnemonicLogicBloc mnemonicLogicBloc =
BlocProvider.of<MnemonicLogicBloc>(context);

mnemonicLogicBloc.outCheckMnemonic.listen((isCorrect){
  if (!isCorrect) {
    SnackBar copySnack = SnackBar(content: Text('Wrong Mnemonic!'));
    _scaffoldKey.currentState.showSnackBar(copySnack);
  }
});

return Scaffold(
    key: _scaffoldKey,
    body: Container(
      width: double.infinity,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Container(
            child: TextField(
              controller: mnemonicController,
            ),
          ),
          RaisedButton(
            onPressed: () {
              mnemonicLogicBloc.isMnemonicCorrect(mnemonicController.text);
            },
            child: Text('Check', style: TextStyle(color: Colors.white)),
          )
        ],
      ),
    ));
}
}

What I want to do is to check, when user press the button, if the string inserted is correct. The method I call in the bloc is:

void isMnemonicCorrect(String typedMnemonic){
if(typedMnemonic == _lastGeneratedMnemonic)
  _inCheckMnemonic.add(true);
else
  _inCheckMnemonic.add(false);
 }

_inCheckMnemonic is the Sink of my Subject (I’m using rxDart), while outCheckMnemonic is my Stream. The problem is that, even though the bloc’s ‘isCorrect’ method is called once, the listen callback is called twice (The SnackBar is shown twice). Why does this happen?

EDIT: I navigate to Page() simply using Navigator.push:

Navigator.of(context).push(MaterialPageRoute(
                            builder: (BuildContext context) {
                return Page();
}));

I’m able to retrieve the bloc because, when my app start I initialized the bloc:

return runApp(BlocProvider<ApplicationBloc>(
bloc: ApplicationBloc(),
child: BlocProvider<MnemonicLogicBloc>(
  bloc: MnemonicLogicBloc(),
  child: BlocProvider<HomePageBloc>(
    bloc: HomePageBloc(),
    child: App(),
  ),
)

));

Solution

When you add a new Widget in your Navigator avoid creating that widget inside the builder, instead declare a variable outside and reuse that variable in your builder, like this:

final page = Page();
Navigator.of(context).push(MaterialPageRoute(
                            builder: (BuildContext context) => page ), );

With this change we avoid that our widget is recreated many times in an unexpected way.

Answered By – diegoveloper

Answer Checked By – Katrina (FlutterFixes Volunteer)

Leave a Reply

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