title: StreamBuilder snapshot.hasError show many times when keyboard show/hide flutter
body: <p>I have a login screen and I'm using BloC pattern, but when i click on button the validation fails, the message from error is called many times, because the stream builder snapshot.error has a value, i don't know how change this to show error only when the user click at the button and validation in fact throw an error.</p>
  
  <p><a href="https://i.stack.imgur.com/1dQdc.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/1dQdc.gif" alt="StreamBuilder snapshot.hasError"></a></p>
  
  <pre><code>class LoginPage extends StatefulWidget {
    static String tag = 'login-page';
  
    @override
    State&lt;StatefulWidget&gt; createState() =&gt; LoginState();
  }
  
  class LoginState extends State&lt;LoginPage&gt; {
    final _usernameController = TextEditingController();
    final _passwordController = TextEditingController();
  
    @override
    Widget build(BuildContext context) {
      LoginBloc loginBloc = BlocProvider.of(context).loginBloc;
  
      return Scaffold(
        body: Container(
            width: MediaQuery.of(context).size.width,
            padding: EdgeInsets.all(16.0),
            decoration: BoxDecoration(
              gradient: LinearGradient(colors: [
                Colors.blueAccent,
                Colors.blue,
              ]),
            ),
            child: Center(
              child: Card(
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.all(Radius.circular(8.0))),
                elevation: 4.0,
                child: ListView(
                  shrinkWrap: true,
                  padding: EdgeInsets.only(left: 16.0, right: 16.0),
                  children: &lt;Widget&gt;[
                    /*_logo(),*/
                    SizedBox(height: 24.0),
                    _emailField(loginBloc),
                    SizedBox(height: 8.0),
                    _passwordField(loginBloc),
                    SizedBox(height: 24.0),
                    _loginButtonSubmit(loginBloc),
                    _loading(loginBloc),
                    _error(loginBloc),
                    _success(loginBloc),
                    _settingsText()
                  ],
                ),
              ),
            )),
      );
    }
  
    Widget _logo() {
      return Hero(
        tag: 'hero',
        child: Padding(
          padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0),
          child: Center(
            child: Container(
              width: 100.0,
              height: 100.0,
              decoration: BoxDecoration(
                image: DecorationImage(
                  fit: BoxFit.fill,
                  image: AssetImage('assets/4.0x/ic_launcher.png'),
                ),
                borderRadius: BorderRadius.all(Radius.circular(50.0)),
              ),
            ),
          ),
        ),
      );
    }
  
    Widget _emailField(LoginBloc loginBloc) {
      return StreamBuilder(
        stream: loginBloc.emailStream,
        builder: (BuildContext context, AsyncSnapshot&lt;dynamic&gt; snapshot) {
          //Anytime the builder sees new data in the emailStream, it will re-render the TextField widget
          return TextField(
            onChanged: loginBloc.setEmail,
            keyboardType: TextInputType.emailAddress,
            controller: _usernameController,
            decoration: InputDecoration(
              labelText: 'Usuário',
              errorText: snapshot
                  .error, //retrieve the error message from the stream and display it
            ),
          );
        },
      );
    }
  
    Widget _passwordField(LoginBloc loginBloc) {
      return StreamBuilder(
        stream: loginBloc.passwordStream,
        builder: (BuildContext context, AsyncSnapshot&lt;dynamic&gt; snapshot) {
          return TextField(
            onChanged: loginBloc.setPassword,
            obscureText: true,
            controller: _passwordController,
            decoration: InputDecoration(
              labelText: 'Senha',
              errorText: snapshot.error,
            ),
          );
        },
      );
    }
  
    Widget _loginButtonSubmit(LoginBloc loginBloc) {
      return Padding(
        padding: EdgeInsets.symmetric(vertical: 16.0),
        child: RaisedButton(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(24),
          ),
          onPressed: () {
            loginBloc.submit(
                LoginRequest(_usernameController.text, _passwordController.text));
          },
          padding: EdgeInsets.all(12),
          color: Colors.blue,
          child: Text('Entrar', style: TextStyle(color: Colors.white)),
        ),
      );
    }
  
    Widget _loading(LoginBloc loginBloc) {
      return StreamBuilder(
          stream: loginBloc.loadingStream,
          initialData: false,
          builder: (BuildContext context, AsyncSnapshot&lt;bool&gt; snapshot) {
            return Center(
              child: snapshot.data
                  ? Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: CircularProgressIndicator(),
                    )
                  : null,
            );
          });
    }
  
    Widget _error(LoginBloc loginBloc) {
      return StreamBuilder(
          stream: loginBloc.successStream,
          builder: (BuildContext context, AsyncSnapshot&lt;dynamic&gt; snapshot) {
            if (snapshot.hasError) {
              _onWidgetDidBuild(() {
                Scaffold.of(context).showSnackBar(SnackBar(
                  content: Text('${snapshot.error}'),
                  backgroundColor: Colors.red,
                ));
              });
            }
            return Container();
          });
    }
  
    Widget _success(LoginBloc loginBloc) {
      return StreamBuilder(
          stream: loginBloc.successStream,
          initialData: null,
          builder: (BuildContext context, AsyncSnapshot&lt;dynamic&gt; snapshot) {
            if (snapshot.hasData &amp;&amp; snapshot.data.erro == 0) {
              _onWidgetDidBuild(() {
                Navigator.of(context).pushReplacement(
                    MaterialPageRoute(builder: (context) =&gt; HomePage()));
              });
            }
            return Container();
          });
    }
  
    Widget _settingsText() {
      return Center(
        child: GestureDetector(
          onTap: () {
            Navigator.of(context).push(
                MaterialPageRoute(builder: (context) =&gt; LoginSettingsPage()));
          },
          child: Padding(
            padding: EdgeInsets.fromLTRB(16.0, 0, 16.0, 16.0),
            child: Text(
              "Configurações",
              style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold),
            ),
          ),
        ),
      );
    }
  
    void _onWidgetDidBuild(Function callback) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        callback();
      });
    }
  }
  </code></pre>
  
  <p><strong>Bloc</strong></p>
  
  <pre><code>class LoginBloc with Validator {
    //RxDart's implementation of StreamController. Broadcast stream by default
    final _emailController = BehaviorSubject&lt;String&gt;();
    final _passwordController = BehaviorSubject&lt;String&gt;();
    final _loadingController = BehaviorSubject&lt;bool&gt;();
    final _successController = BehaviorSubject&lt;LoginResponse&gt;();
    final _submitController = PublishSubject&lt;LoginRequest&gt;();
  
    //Return the transformed stream
    Stream&lt;String&gt; get emailStream =&gt; _emailController.stream.transform(performEmptyEmailValidation);
    Stream&lt;String&gt; get passwordStream =&gt; _passwordController.stream.transform(performEmptyPasswordValidation);
    Stream&lt;bool&gt; get loadingStream =&gt; _loadingController.stream;
    Stream&lt;LoginResponse&gt; get successStream =&gt; _successController.stream;
  
    //Add data to the stream
    Function(String) get setEmail =&gt; _emailController.sink.add;
    Function(String) get setPassword =&gt; _passwordController.sink.add;
    Function(LoginRequest) get submit =&gt; _submitController.sink.add;
  
    LoginBloc() {
      _submitController.stream.distinct().listen((request) {
        _login(request.username, request.password);
      });
    }
  
    _login(String useName, String password) async {
      _loadingController.add(true);
  
      ApiService.login(useName, password).then((response) {
        if (response.erro == 0) {
          saveResponse(response);
        } else {
          final error = Utf8Codec().decode(base64.decode(response.mensagem));
          _successController.addError(error);
          print(error);
        }
        _loadingController.add(false);
      }).catchError((error) {
        print(error);
        _loadingController.add(false);
        _successController.addError("Falha ao realizar login!");
      });
    }
  
    saveResponse(LoginResponse response) {
      SharedPreferences.getInstance().then((preferences) async {
        var urlSaved = await preferences.setString(
            Constants.LOGIN_RESPONSE, response.toJson().toString());
        if (urlSaved) {
          _successController.add(response);
        }
      }).catchError((error) {
        _successController.addError(error);
      });
    }
  
    dispose() {
      _emailController.close();
      _passwordController.close();
      _loadingController.close();
      _successController.close();
      _submitController.close();
    }
  }
  </code></pre>