如何在Flutter App中正确使用BlocListener和BlocProvider

K.c*_*him 8 flutter flutter-packages flutter-bloc

我在 Flutter 应用程序中使用 flutter_bloc 4.0.0,我使用 Felix Angelov 的示例(https://medium.com/flutter-community/firebase-login-with-flutter-bloc-47455e6047b0)来实现登录或使用块模式的登录流程。它工作正常,但在我更新我的 Flutter 并检查我的代码后,我发现了一系列错误。我不明白他们为什么要来,因为上周一切都很好。小部件的构建方法中的块的实现对我来说突然变得错误了。我收到错误:

\n\n

1.“BlocListener的\xe2\x80\x98A值类型不能从方法构建中返回\xe2\x80\x99,因为它有一个widget的返回类型”

\n\n
    \n
  1. \xe2\x80\x98BlocProvider> 的值类型可以从方法构建中返回\xe2\x80\x99,因为它的返回类型为 widget\xe2\x80\x99
  2. \n
\n\n

第一个错误的代码

\n\n
class LoginForm extends StatefulWidget {\n  final UserRepository _userRepository;\n\n  LoginForm({Key key, @required UserRepository userRepository})\n      : assert(userRepository != null),\n        _userRepository = userRepository,\n        super(key: key);\n\n  State<LoginForm> createState() => _LoginFormState();\n}\n\nclass _LoginFormState extends State<LoginForm> {\n  final TextEditingController _emailController = TextEditingController();\n  final TextEditingController _passwordController = TextEditingController();\n\n  LoginBloc _loginBloc;\n\n  UserRepository get _userRepository => widget._userRepository;\n\n  bool get isPopulated =>\n      _emailController.text.isNotEmpty && _passwordController.text.isNotEmpty;\n\n  bool isLoginButtonEnabled(LoginState state) {\n    return state.isFormValid && isPopulated && !state.isSubmitting;\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    _loginBloc = BlocProvider.of<LoginBloc>(context);\n    _emailController.addListener(_onEmailChanged);\n    _passwordController.addListener(_onPasswordChanged);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return BlocListener<LoginBloc, LoginState>(\n      listener: (context, state) {\n        if (state.isFailure) {\n          Scaffold.of(context)\n            ..hideCurrentSnackBar()\n            ..showSnackBar(\n              SnackBar(\n                content: Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                  children: [Text(\'Login Failure\'), Icon(Icons.error)],\n                ),\n                backgroundColor: Colors.red,\n              ),\n            );\n        }\n        if (state.isSubmitting) {\n          Scaffold.of(context)\n            ..hideCurrentSnackBar()\n            ..showSnackBar(\n              SnackBar(\n                content: Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                  children: [\n                    Text(\'Logging In...\'),\n                    CircularProgressIndicator(),\n                  ],\n                ),\n              ),\n            );\n        }\n        if (state.isSuccess) {\n          BlocProvider.of<AuthenticationBloc>(context).add(LoggedIn());\n        }\n      },\n      child: BlocBuilder<LoginBloc, LoginState>(\n        builder: (context, state) {\n          return Padding(\n            padding: EdgeInsets.all(20.0),\n            child: Form(\n              child: ListView(\n                children: <Widget>[\n                  Padding(\n                    padding: EdgeInsets.symmetric(vertical: 20),\n                    child: Image.asset(\'assets/flutter_logo.png\', height: 200),\n                  ),\n                  Container(\n                    margin: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 20.0),\n                    height: 45.0,\n                    child: TextFormField(\n                      controller: _emailController,\n                      style: TextStyle(\n                        fontFamily: \'Avenir-Medium\',\n                        fontSize: 12.0,\n                        color: Colors.black,\n                      ),\n                      decoration: InputDecoration(\n                        border: OutlineInputBorder(\n                            borderRadius: const BorderRadius.all(\n                              const Radius.circular(7.0),\n                            ),\n                            borderSide: BorderSide(\n                              color: Colors.grey[200],\n                              width: 7.0,\n                            )),\n                        labelText: \'email\',\n                      ),\n                      keyboardType: TextInputType.emailAddress,\n                      autovalidate: true,\n                      autocorrect: false,\n                      validator: (_) {\n                        return !state.isEmailValid ? \'Invalid Email\' : null;\n                      },\n                    ),\n                  ),\n                  Container(\n                    height: 45.0,\n                    child: TextFormField(\n                      style: TextStyle(\n                        fontFamily: \'Avenir-Medium\',\n                        fontSize: 12.0,\n                        color: Colors.black,\n                      ),\n                      controller: _passwordController,\n                      decoration: InputDecoration(\n                        border: OutlineInputBorder(\n                            borderRadius: const BorderRadius.all(\n                              const Radius.circular(7.0),\n                            ),\n                            borderSide: BorderSide(\n                              color: Colors.grey[200],\n                              width: 0.0,\n                            )),\n                        labelText: \'password\',\n                      ),\n                      obscureText: true,\n                      autovalidate: true,\n                      autocorrect: false,\n                      validator: (_) {\n                        return !state.isPasswordValid ? \'Invalid Password\' : null;\n                      },\n                    ),\n                  ),\n                  Padding(\n                    padding: EdgeInsets.symmetric(vertical: 20),\n                    child: Column(\n                      crossAxisAlignment: CrossAxisAlignment.stretch,\n                      children: <Widget>[\n                        LoginButton(\n                          onPressed: _onFormSubmitted,\n\n//                          isLoginButtonEnabled(state)\n//                              ? _onFormSubmitted\n//                              : null,\n                        ),\n                        GoogleLoginButton(),\n                        AppleSignInButton(),\n                        CreateAccountButton(userRepository: _userRepository),\n                        ForgotPasswordButton()\n                      ],\n                    ),\n                  ),\n                ],\n              ),\n            ),\n          );\n        },\n      ),\n    );\n  }\n\n  @override\n  void dispose() {\n    _emailController.dispose();\n    _passwordController.dispose();\n    super.dispose();\n  }\n\n  void _onEmailChanged() {\n    _loginBloc.add(\n      EmailChanged(email: _emailController.text),\n    );\n  }\n\n  void _onPasswordChanged() {\n    _loginBloc.add(\n      PasswordChanged(password: _passwordController.text),\n    );\n  }\n\n  void _onFormSubmitted() {\n    _loginBloc.add(\n      LoginWithCredentialsPressed(\n        email: _emailController.text,\n        password: _passwordController.text,\n      ),\n    );\n  }\n}\n\nthe code for the second error above is as follows\n\nvoid main() {\n  WidgetsFlutterBinding.ensureInitialized();\n  BlocSupervisor.delegate = SimpleBlocDelegate();\n  final UserRepository userRepository = UserRepository();\n  runApp(\n    BlocProvider(\n      create: (context) => AuthenticationBloc(\n        userRepository: userRepository,\n      )..add(AppStarted()),\n      child: App(userRepository: userRepository),\n    ),\n  );\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

错误的图像

\n\n

第二个错误

\n

Tho*_*mas 26

也许我的回答对你来说有点过时,但我希望它能对其他人有所帮助。

首先,BlocBuilder/BlocListener 应该在相应的 BlocProvider 的范围内。

您应该在无状态/有状态小部件的构建方法中返回 BlocBuilder。如果你想结合 BlocListener 和 BlocBuilder,你可以使用 BlocConsumer 小部件。此外,您可以添加参数 buildWhen 来指定 BlocBuilder 是否应根据传入状态重建您的小部件。这是一个例子:

class __ScreenWidgetState extends State<Screen> {

  @override
  Widget build(BuildContext context) {
    return BlocConsumer<ScreenBloc, ScreenState>(
      buildWhen: (previousState, state) {
        return state is! DontBuild;
      },
      builder: (BuildContext context, state) {
        return Text(state.text);
      },
      listener: (BuildContext context, state) {
        if (state is ShowFlushbar) {
          showFlushBar(context: context, message: state.text);
        }
      },
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

因此,我们的 Screen Widget 应该位于 ScreenBloc 的范围内(作为它的子级)。我们可以通过以下方式实现这一点:

BlocProvider<ScreenBloc>(
  create: (context) => ScreenBloc(),
  child: Screen(),
);
Run Code Online (Sandbox Code Playgroud)