StreamBuilder snapshot.hasError показывает много раз, когда клавиатура показывает / скрывает флаттер - PullRequest
0 голосов
/ 03 января 2019

У меня есть экран входа в систему, и я использую шаблон BloC, но когда я нажимаю на кнопку, проверка не проходит, сообщение об ошибке вызывается много раз, потому что потоковый снимок snapshot.error имеет значение, я незнать, как изменить это, чтобы показывать ошибку только тогда, когда пользователь нажимает на кнопку и проверка фактически выдает ошибку.

StreamBuilder snapshot.hasError

class LoginPage extends StatefulWidget {
  static String tag = 'login-page';

  @override
  State<StatefulWidget> createState() => LoginState();
}

class LoginState extends State<LoginPage> {
  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: <Widget>[
                  /*_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<dynamic> 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<dynamic> 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<bool> 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<dynamic> 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<dynamic> snapshot) {
          if (snapshot.hasData && snapshot.data.erro == 0) {
            _onWidgetDidBuild(() {
              Navigator.of(context).pushReplacement(
                  MaterialPageRoute(builder: (context) => HomePage()));
            });
          }
          return Container();
        });
  }

  Widget _settingsText() {
    return Center(
      child: GestureDetector(
        onTap: () {
          Navigator.of(context).push(
              MaterialPageRoute(builder: (context) => 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();
    });
  }
}

Блок

class LoginBloc with Validator {
  //RxDart's implementation of StreamController. Broadcast stream by default
  final _emailController = BehaviorSubject<String>();
  final _passwordController = BehaviorSubject<String>();
  final _loadingController = BehaviorSubject<bool>();
  final _successController = BehaviorSubject<LoginResponse>();
  final _submitController = PublishSubject<LoginRequest>();

  //Return the transformed stream
  Stream<String> get emailStream => _emailController.stream.transform(performEmptyEmailValidation);
  Stream<String> get passwordStream => _passwordController.stream.transform(performEmptyPasswordValidation);
  Stream<bool> get loadingStream => _loadingController.stream;
  Stream<LoginResponse> get successStream => _successController.stream;

  //Add data to the stream
  Function(String) get setEmail => _emailController.sink.add;
  Function(String) get setPassword => _passwordController.sink.add;
  Function(LoginRequest) get submit => _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();
  }
}

1 Ответ

0 голосов
/ 04 января 2019

Я нашел решение своей ошибки, когда при нажатии в InputField и изменении фокуса, виджет перестройки StreamBuilder e снова показывает ошибку каждый раз.Я просто поставил валидацию перед запуском, показывает ошибку, чтобы рассмотреть состояние снимка.

  Widget _error(LoginBloc loginBloc) {
    return StreamBuilder(
        stream: loginBloc.successStream,
        builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
          if (snapshot.connectionState == ConnectionState.active &&
              snapshot.hasError) {
            _onWidgetDidBuild(() {
              Scaffold.of(context).showSnackBar(SnackBar(
                content: Text('${snapshot.error}'),
                backgroundColor: Colors.red,
              ));
            });
          }
          return Container();
        });
  }

Если активен, потому что я выбрасываю ошибку в своем классе Bloc, если нет, это потому, что построитель потока былперестроил виджет.Это решает мою проблему.Я не знаю, является ли это лучшим решением, но сейчас решает мою проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...