草庐IT

页面重新加载后 Flutter StreamBuilder 不工作

coder 2023-07-24 原文

我的应用程序中有一个带有 TextField 表单验证器的注册页面,带有一个按钮。如果未满足业务规则,文本字段将显示表单验证错误消息,并且一旦满足所有条件,“下一步”按钮将可点击。这目前在我的应用程序中运行良好,但我发现一旦我离开页面并返回到它,验证错误消息就会停止显示并且按钮也会停止工作。查看我的 IDE (android studio) 中的控制台日志,我收到的唯一相关错误消息是

[VERBOSE-2:ui_dart_state.cc(148)] Unhandled Exception: Bad state: 
Stream is already closed
#0      _SinkTransformerStreamSubscription._add 
(dart:async/stream_transformers.dart:66:7)
#1      _EventSinkWrapper.add 
(dart:async/stream_transformers.dart:15:11)

我不太确定这是什么意思,在页面重新加载后流是否关闭而不是重新打开?如果没有,有没有办法解决这个问题或者我缺少什么? This is我正在经历什么

流生成器代码:

    Widget emailField(authBloc) {
    return StreamBuilder(
      stream: authBloc.emailStream,
      builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
        return TextField(
          onChanged: authBloc.updateEmail,
          keyboardType: TextInputType.emailAddress,
          decoration: InputDecoration(
              border: UnderlineInputBorder(
                borderSide: BorderSide(
                  color: Colors.deepOrange
                )
              ),
              hintText: 'Enter Email',
              labelText: 'Email Address',
              errorText: snapshot.error
          ),
        );
      },
    );
  }

  Widget passwordField( authBloc) {
    return StreamBuilder(
      stream: authBloc.passwordStream,
      builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
        return TextField(
          onChanged: authBloc.updatePassword,
          obscureText: true,
          decoration: InputDecoration(
            hintText: 'Enter password',
            labelText: 'Password',
            errorText: snapshot.error,
          ),
        );
      },
    );
  }


  Widget checkPasswordField( authBloc) {
    return StreamBuilder(
      stream: authBloc.validatePasswordStream,
      builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
        return TextField(
          onChanged: authBloc.updateValidatePassword,
          obscureText: true,
          decoration: InputDecoration(
            hintText: 'Re-enter password',
            labelText: 'Confirm Password',
            errorText: snapshot.error,
          ),
        );
      },
    );
  }

  Widget nextBtn(authBloc) {
    return StreamBuilder(
        stream: authBloc.submitValid,
        builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
          return RaisedButton(
            child: Text('Next'),
            color: Colors.deepOrange,
            shape: BeveledRectangleBorder(
              borderRadius: BorderRadius.all(Radius.circular(7.0))
            ),
            onPressed: snapshot.hasData
                ? () => Navigator.pushNamed(context, '/register')
            : null,
          );
        }
    );
  }

流:

       /// REGISTER VARIABLES
  static final _emailController = BehaviorSubject<
      String>(); //RxDart's implementation of StreamController. Broadcast stream by default
  static final _passwordController = BehaviorSubject<String>();
  static final _validatePasswordController = BehaviorSubject<
      String>(); // Will check that the password entered the 2nd time is correct

 /// REGISTER STREAM & METHODS
  //Retrieve data from the stream
  Stream<String> get emailStream => _emailController.stream
      .transform(performEmailValidation); //Return the transformed stream

  Stream<String> get passwordStream =>
      _passwordController.stream.transform(performPasswordValidation);

  Stream<String> get validatePasswordStream =>
      _validatePasswordController.stream.transform(performIsPasswordSame);

 //Merging email, password and validate password
  Stream<bool> get submitValid => Observable.combineLatest3(
      emailStream, passwordStream, validatePasswordStream, (e, p1, p2) => true);

//Add data to the stream
  Function(String) get updateEmail => _emailController.sink.add;
  Function(String) get updatePassword => _passwordController.sink.add;
  Function(String) get updateValidatePassword =>
      _validatePasswordController.sink.add;

// performing user input validations
  final performEmailValidation = StreamTransformer<String, String>.fromHandlers(
      handleData: (email, sink) async {
    String emailValidationRule =
        r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
    RegExp regExp = new RegExp(emailValidationRule);
    if (await doesNameAlreadyExist("email", _emailController.value) == true)
      sink.addError("That email already exists");
    else if (regExp.hasMatch(email)) {
      sink.add(email);
    } else {
      sink.addError(StringConstant.emailErrorMessage);
    }
  });

  final performPasswordValidation =
      StreamTransformer<String, String>.fromHandlers(
          handleData: (_passwordController, sink) {
    if (_passwordController.length >= 6) {
      sink.add(_passwordController);
    } else {
      sink.addError(StringConstant.passwordErrorMessage);
    }
  });

  final performIsPasswordSame = StreamTransformer<String, String>.fromHandlers(
      handleData: (password, sink) {
    if (password != _passwordController.value)
      sink.addError(StringConstant.invalidPasswordMessage);
    else
      sink.add(password);
  });

整个屏幕代码:

class SignUp extends StatefulWidget {
  @override
  _SignUpState createState() => _SignUpState();
}



class _SignUpState extends State<SignUp> {
  AuthBloc _authBloc;



  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _authBloc = AuthBlocProvider.of(context);
  }




  @override
  Widget build(BuildContext context) {


    return Scaffold(

      body: Container(
          alignment: Alignment.center,
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              children: <Widget>[
                SizedBox(height: 80,),
                Text("Register", style: Style.appTextStyle),
                SizedBox(height: 100,),
                emailField(_authBloc),
                SizedBox(height: 30),
                passwordField(_authBloc),
                SizedBox(height: 30),
                checkPasswordField(_authBloc),
                SizedBox(height: 30),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    cancelBtn(),

                    nextBtn(_authBloc),

                  ],
                )

                // checkPasswordField(authBloc),
              ],
            ),
          )
      ),
    );
  }

  Widget emailField(authBloc) {
    return StreamBuilder(
      stream: authBloc.emailStream,
      builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
        return TextField(
          onChanged: authBloc.updateEmail,
          keyboardType: TextInputType.emailAddress,
          decoration: InputDecoration(
              border: UnderlineInputBorder(
                borderSide: BorderSide(
                  color: Colors.deepOrange
                )
              ),
              hintText: 'Enter Email',
              labelText: 'Email Address',
              errorText: snapshot.error
          ),
        );
      },
    );
  }

  Widget passwordField( authBloc) {
    return StreamBuilder(
      stream: authBloc.passwordStream,
      builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
        return TextField(
          onChanged: authBloc.updatePassword,
          obscureText: true,
          decoration: InputDecoration(
            hintText: 'Enter password',
            labelText: 'Password',
            errorText: snapshot.error,
          ),
        );
      },
    );
  }


  Widget checkPasswordField( authBloc) {
    return StreamBuilder(
      stream: authBloc.validatePasswordStream,
      builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
        return TextField(
          onChanged: authBloc.updateValidatePassword,
          obscureText: true,
          decoration: InputDecoration(
            hintText: 'Re-enter password',
            labelText: 'Confirm Password',
            errorText: snapshot.error,
          ),
        );
      },
    );
  }

  Widget nextBtn(authBloc) {
    return StreamBuilder(
        stream: authBloc.submitValid,
        builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
          return RaisedButton(
            child: Text('Next'),
            color: Colors.deepOrange,
            shape: BeveledRectangleBorder(
              borderRadius: BorderRadius.all(Radius.circular(7.0))
            ),
            onPressed: snapshot.hasData
                ? () => Navigator.pushNamed(context, '/register')
            : null,
          );
        }
    );
  }

  Widget cancelBtn(){
    return RaisedButton(
      child: Text('Cancel'),
      color: Colors.white30,
      shape: BeveledRectangleBorder(
        borderRadius: BorderRadius.all(Radius.circular(7.0))
      ),
      onPressed: () => Navigator.pop(context),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _authBloc.dispose();
  }

Bloc 代码:

/// REGISTER VARIABLES
      static final _emailController = BehaviorSubject<
          String>(); //RxDart's implementation of StreamController. Broadcast stream by default
      static final _passwordController = BehaviorSubject<String>();
      static final _validatePasswordController = BehaviorSubject<
          String>(); // Will check that the password entered the 2nd time is correct

     /// REGISTER STREAM & METHODS
      //Retrieve data from the stream
      Stream<String> get emailStream => _emailController.stream
          .transform(performEmailValidation); //Return the transformed stream

      Stream<String> get passwordStream =>
          _passwordController.stream.transform(performPasswordValidation);

      Stream<String> get validatePasswordStream =>
          _validatePasswordController.stream.transform(performIsPasswordSame);

     //Merging email, password and validate password
      Stream<bool> get submitValid => Observable.combineLatest3(
          emailStream, passwordStream, validatePasswordStream, (e, p1, p2) => true);

    //Add data to the stream
      Function(String) get updateEmail => _emailController.sink.add;
      Function(String) get updatePassword => _passwordController.sink.add;
      Function(String) get updateValidatePassword =>
          _validatePasswordController.sink.add;

    // performing user input validations
      final performEmailValidation = StreamTransformer<String, String>.fromHandlers(
          handleData: (email, sink) async {
        String emailValidationRule =
            r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
        RegExp regExp = new RegExp(emailValidationRule);
        if (await doesNameAlreadyExist("email", _emailController.value) == true)
          sink.addError("That email already exists");
        else if (regExp.hasMatch(email)) {
          sink.add(email);
        } else {
          sink.addError(StringConstant.emailErrorMessage);
        }
      });

      final performPasswordValidation =
          StreamTransformer<String, String>.fromHandlers(
              handleData: (_passwordController, sink) {
        if (_passwordController.length >= 6) {
          sink.add(_passwordController);
        } else {
          sink.addError(StringConstant.passwordErrorMessage);
        }
      });

      final performIsPasswordSame = StreamTransformer<String, String>.fromHandlers(
          handleData: (password, sink) {
        if (password != _passwordController.value)
          sink.addError(StringConstant.invalidPasswordMessage);
        else
          sink.add(password);
      });

    dispose() {
        _emailController.close();
        _passwordController.close();
        _validatePasswordController.close();

    }

最佳答案

好吧,查看完整的源代码和您展示的 GIF,我可以看出是什么导致了这个问题。 您的错误是在 dispose() SingUp 小部件类方法中调用了 dispose() BLoC 实例方法。

为什么是错误的?

在您的特定情况下,当您处于 SingUp 屏幕并转到下一个路由/屏幕时,将调用来自 SingUp 的 dispose 方法,此时您的 BLoC 实例的流将被关闭.但是下一个允许用户返回到 SingUp 屏幕,当这种情况发生时,SingUp 实例获得与之前使用的相同的 BLoC 实例,但是这个 BLoC 实例的流已经关闭。

我怎样才能以简单的方式解决这个问题?

在 SingUp 类中:

@override
  void dispose() {
    super.dispose();
   /// DON'T CALL BLoC dispose here
   /// _authBloc.dispose();
  }

不要在这里放置你的 BloC,因为用户可以随时返回到这个屏幕。由于您正在使用 InheritedWidget 获取 BLoC 实例,这使您可以在不同的地方访问相同的 BLoC 实例,我建议您在用户结束所有调用时调用 yourBloc.dispose()过程。

关于页面重新加载后 Flutter StreamBuilder 不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55132741/

有关页面重新加载后 Flutter StreamBuilder 不工作的更多相关文章

  1. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  2. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  3. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  4. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

    我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

  5. ruby-on-rails - active_admin 目录中的常量警告重新声明 - 2

    我正在使用active_admin,我在Rails3应用程序的应用程序中有一个目录管理,其中包含模型和页面的声明。时不时地我也有一个类,当那个类有一个常量时,就像这样:classFooBAR="bar"end然后,我在每个必须在我的Rails应用程序中重新加载一些代码的请求中收到此警告:/Users/pupeno/helloworld/app/admin/billing.rb:12:warning:alreadyinitializedconstantBAR知道发生了什么以及如何避免这些警告吗? 最佳答案 在纯Ruby中:classA

  6. ruby-on-rails - rspec should have_select ('cars' , :options => ['volvo' , 'saab' ] 不工作 - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion在首页我有:汽车:VolvoSaabMercedesAudistatic_pages_spec.rb中的测试代码:it"shouldhavetherightselect"dovisithome_pathit{shouldhave_select('cars',:options=>['volvo','saab','mercedes','audi'])}end响应是rspec./spec/request

  7. ruby-on-rails - s3_direct_upload 在生产服务器中不工作 - 2

    在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo

  8. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  9. ruby - 在 Ruby 中重新分配常量时抛出异常? - 2

    我早就知道Ruby中的“常量”(即大写的变量名)不是真正常量。与其他编程语言一样,对对象的引用是唯一存储在变量/常量中的东西。(侧边栏:Ruby确实具有“卡住”引用对象不被修改的功能,据我所知,许多其他语言都没有提供这种功能。)所以这是我的问题:当您将一个值重新分配给常量时,您会收到如下警告:>>FOO='bar'=>"bar">>FOO='baz'(irb):2:warning:alreadyinitializedconstantFOO=>"baz"有没有办法强制Ruby抛出异常而不是打印警告?很难弄清楚为什么有时会发生重新分配。 最佳答案

  10. ruby-on-rails - 使用 config.threadsafe 时从 lib/加载模块/类的正确方法是什么!选项? - 2

    我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co

随机推荐