Saya membuat fitur login, saya membuat respons api untuk mengetahui apakah respons berhasil atau gagal. dapat melihat kode BLOC saya. proses login berhasil tetapi ketika saya ingin kembali ke halaman login setelah logout muncul error Unhandled Exception: Bad state: Cannot add new events after calling close. bagaimana saya bisa mengatasinya?

RESPON API:

class ApiResponse<T> {
  Status status;
  T data;
  String message;

  ApiResponse.loading(this.message) : status = Status.LOADING;

  ApiResponse.completed(this.data) : status = Status.COMPLETED;

  ApiResponse.error(this.message) : status = Status.ERROR;

//  @override
//  String toString() {
//    return "Status : $status \n Message : $message \n Data : $data";
//  }
}

enum Status { LOADING, COMPLETED, ERROR }

BLOK:

class LoginBloc extends Object with Validators{
  final _repository = EresidenceRepository();
  final _userid = BehaviorSubject<String>();
  final _password = BehaviorSubject<String>();
  final _imei = BehaviorSubject<String>();
  final _coordinate = BehaviorSubject<String>();
  final BehaviorSubject<ApiResponse<login_responses>> _subject = BehaviorSubject<ApiResponse<login_responses>>();

  Function(String) get userid => _userid.sink.add;
  Function(String) get password => _password.sink.add;
  Function(String) get imei => _imei.sink.add;
  Function(String) get coordinate => _coordinate.sink.add;

  Stream<String> get useridValidation => _userid.stream.transform(useridValidator);
  Stream<String> get passwordValidation => _password.stream.transform(passwordValidator);
  Stream<bool> get submitCheck => Rx.combineLatest2(useridValidation, passwordValidation, (e,p) => true);

  login() async {
    _subject.sink.add(ApiResponse.loading("Logging In..."));
    try {
      login_responses response = await _repository.login(
          _userid.value, _password.value, _imei.value, _coordinate.value);

      prefsBloc.changePrefsLogin(
          PrefsState(false, response.data.userid, response.data.password, _imei.value, _coordinate.value, "")
      );

      _subject.sink.add(ApiResponse.completed(response));

      print(response);

    } catch (e) {
      _subject.sink.add(ApiResponse.error(e.toString()));
      print(e);
    }
  }

  dispose(){
    _userid.close();
    _password.close();
    _imei.close();
    _coordinate.close();
    _subject.close();
  }

  BehaviorSubject<ApiResponse<login_responses>> get subject => _subject;

}

final login = LoginBloc();

UI:

class LoginPage extends StatefulWidget {
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> with WidgetsBindingObserver{

  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
  FocusNode passwordFocusNode, useridFocusNode;

  @override
  void initState() {
    super.initState();

    prefsBloc.checkLoginPref(context);


    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {

    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }


  @override
  Widget build(BuildContext context) {
    return AnnotatedRegion<SystemUiOverlayStyle>(
        value: SystemUiOverlayStyle.dark,
        child: Scaffold(
          backgroundColor: Colors.white,
          body: StreamBuilder(
              stream: login.subject,
              builder: (context, AsyncSnapshot<ApiResponse<login_responses>> snapshot){
                if(snapshot.hasData) {
                  print(snapshot.data.status);
                  switch (snapshot.data.status) {
                    case Status.LOADING:
                      _onWidgetDidBuild((){
                        Scaffold.of(context).showSnackBar(
                            SnackBar(
                              content: Row(
                                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                children: [
                                  Text(snapshot.data.message),
                                  CircularProgressIndicator(),
                                ],
                              ),
                              backgroundColor: Colors.black,
                            ),
                          );
                      });
                      break;
                    case Status.COMPLETED:
                      login_responses result = snapshot.data.data;
                      if(result.data.bit70 == "000") {
                        _onWidgetDidBuild((){
                          login.dispose();
                          AppRoutes.replace(context, LoginVerifyPage());
                        });
                      }else{
                        _onWidgetDidBuild((){
                          login.dispose();
                          AppRoutes.replace(context, MainApp());
                        });
                      }
                      break;
                    case Status.ERROR:
                      _onWidgetDidBuild(() {
                        Scaffold.of(context).showSnackBar(SnackBar(
                          content: Text('${snapshot.data.message}'),
                          backgroundColor: Colors.red,
                        ));
                      });
                      break;
                  }
                }
                return _formLogin();
              }
          ),
        )
    );
  }

  _formLogin() {
    return SafeArea(
      child: Container(
        child: Column(
          children: <Widget>[
            Expanded(
                flex: 1,
                child: Container(
                    padding: EdgeInsets.symmetric(horizontal: SizeConfig.widthMultiplier * 1, vertical: SizeConfig.heightMultiplier * 1),
                    child: CachedNetworkImage(
                      imageUrl: "https://images.glints.com/unsafe/1024x0/glints-dashboard.s3.amazonaws.com/company-logo/68545821966f833d182f98775c73c7ae.png",
                      errorWidget: (context, url, error) => Icon(Icons.broken_image),
                      fit: BoxFit.fill,
                    ),
                )
            ),
            Expanded(
                flex: 2,
                child: Container(
                    padding: EdgeInsets.all(SizeConfig.heightMultiplier * 2),
                    child: Column(
                      children: <Widget>[
                        Container(
                            child: StreamBuilder<String>(
                              stream: login.useridValidation,
                              builder: (context, snapshot) => DataTextField(
                                errorText: snapshot.error,
                                hintText: "No Handphone",
                                textInputAction: TextInputAction.next,
                                icon: Icons.phone,
                                onSubmitted: () => FocusScope.of(context).requestFocus(passwordFocusNode),
                                onChanged: login.userid,
                                keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true),
                              ),
                            )
                        ),
                        Container(
                            margin: EdgeInsets.only(top: SizeConfig.heightMultiplier * 2),
                            child: StreamBuilder<String>(
                              stream: login.passwordValidation,
                              builder: (context, snapshot) => PasswordTextField(
                              errorText: snapshot.error,
                                hintText: "Password",
                                textInputAction: TextInputAction.done,
                                onSubmitted: () {
                                  FocusScope.of(context).requestFocus(FocusNode());
                                },
                                onChanged: login.password,
                                focusNode: passwordFocusNode,
                              ),
                            )
                        ),
                        Container(
                            width: SizeConfig.screenWidth,
                            margin: EdgeInsets.only(top: SizeConfig.heightMultiplier * 2.5),
                            child: GestureDetector(
                              onTap: () => AppRoutes.push(context, ForgotPasswordPage()),
                              child: Text(
                                Strings.titleForgotPass+" ?",
                                style: AppTheme.styleSubTitlePurpel,
                                textAlign: TextAlign.right,
                              ),
                            )
                        ),
                        Container(
                          width: SizeConfig.screenWidth,
                          margin: EdgeInsets.only(top: SizeConfig.heightMultiplier * 5),
                          child: StreamBuilder<bool>(
                            stream: login.submitCheck,
                            builder: (context, snapshot) => AppButton(
                                onPressed: snapshot.hasData ? () => login.login() : null,
                                text: Strings.signin
                            ),
                          )
                        )
                      ],
                    )
                )
            ),
            Expanded(
                flex: 1,
                child: Container(
                    alignment: Alignment.bottomCenter,
                    margin: EdgeInsets.only(bottom: SizeConfig.heightMultiplier * 2.5),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        Text(
                          Strings.dontAccount,
                          style: AppTheme.styleSubTitleBlackSmall,
                          textAlign: TextAlign.right,
                        ),
                        Container(
                            margin: EdgeInsets.only(left: SizeConfig.widthMultiplier * 1),
                            child: InkWell(
                              onTap: () => AppRoutes.push(context, RegistrationPage()),
                              child: Text(
                                Strings.registration,
                                style: AppTheme.styleSubTitlePurpel,
                                textAlign: TextAlign.right,
                              ),
                            )
                        )
                      ],
                    )
                )
            )
          ],
        ),
      ),
    );
  }

  void _onWidgetDidBuild(Function callback) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      callback();
    });
  }
}
1
Jsoon 23 April 2020, 07:01

1 menjawab

Jawaban Terbaik

Masalahnya adalah Anda telah membuat instance dari blok Anda secara global (yang bukan merupakan praktik yang baik), dan setelah proses login selesai, Anda telah memanggil login.dispose() yang menutup semua aliran di LoginBloc Anda, dan Anda dapat 't menambahkan acara baru ke aliran tertutup.

Anda sebaiknya membuat instance LoginBloc Anda dalam metode LoginPage initState, dan menutupnya dalam metode dispose. Dengan cara ini, setiap kali Anda menavigasi ke halaman login, blok baru dibuat dan akan berfungsi seperti yang diharapkan.

PEMBARUAN: Contoh sederhana:

class LoginPage extends StatefulWidget {
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  LoginBloc _loginBloc;

  @override
  void initState() {
    super.initState();
    _loginBloc = LoginBloc();
  }

  @override
  void dispose() {
    _loginBloc.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}
2
Ali Alizadeh 23 April 2020, 06:55