繁体   English   中英

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

[英]How to correctly use BlocListener and BlocProvider in Flutter App

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

1."'BlocListener 的值类型无法从方法 build 返回,因为它具有 widget 的返回类型"

  1. 'BlocProvider> 的值类型无法从方法构建中返回,因为它具有小部件的返回类型'

第一个错误的代码

class LoginForm extends StatefulWidget {
  final UserRepository _userRepository;

  LoginForm({Key key, @required UserRepository userRepository})
      : assert(userRepository != null),
        _userRepository = userRepository,
        super(key: key);

  State<LoginForm> createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  LoginBloc _loginBloc;

  UserRepository get _userRepository => widget._userRepository;

  bool get isPopulated =>
      _emailController.text.isNotEmpty && _passwordController.text.isNotEmpty;

  bool isLoginButtonEnabled(LoginState state) {
    return state.isFormValid && isPopulated && !state.isSubmitting;
  }

  @override
  void initState() {
    super.initState();
    _loginBloc = BlocProvider.of<LoginBloc>(context);
    _emailController.addListener(_onEmailChanged);
    _passwordController.addListener(_onPasswordChanged);
  }

  @override
  Widget build(BuildContext context) {
    return BlocListener<LoginBloc, LoginState>(
      listener: (context, state) {
        if (state.isFailure) {
          Scaffold.of(context)
            ..hideCurrentSnackBar()
            ..showSnackBar(
              SnackBar(
                content: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [Text('Login Failure'), Icon(Icons.error)],
                ),
                backgroundColor: Colors.red,
              ),
            );
        }
        if (state.isSubmitting) {
          Scaffold.of(context)
            ..hideCurrentSnackBar()
            ..showSnackBar(
              SnackBar(
                content: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text('Logging In...'),
                    CircularProgressIndicator(),
                  ],
                ),
              ),
            );
        }
        if (state.isSuccess) {
          BlocProvider.of<AuthenticationBloc>(context).add(LoggedIn());
        }
      },
      child: BlocBuilder<LoginBloc, LoginState>(
        builder: (context, state) {
          return Padding(
            padding: EdgeInsets.all(20.0),
            child: Form(
              child: ListView(
                children: <Widget>[
                  Padding(
                    padding: EdgeInsets.symmetric(vertical: 20),
                    child: Image.asset('assets/flutter_logo.png', height: 200),
                  ),
                  Container(
                    margin: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 20.0),
                    height: 45.0,
                    child: TextFormField(
                      controller: _emailController,
                      style: TextStyle(
                        fontFamily: 'Avenir-Medium',
                        fontSize: 12.0,
                        color: Colors.black,
                      ),
                      decoration: InputDecoration(
                        border: OutlineInputBorder(
                            borderRadius: const BorderRadius.all(
                              const Radius.circular(7.0),
                            ),
                            borderSide: BorderSide(
                              color: Colors.grey[200],
                              width: 7.0,
                            )),
                        labelText: 'email',
                      ),
                      keyboardType: TextInputType.emailAddress,
                      autovalidate: true,
                      autocorrect: false,
                      validator: (_) {
                        return !state.isEmailValid ? 'Invalid Email' : null;
                      },
                    ),
                  ),
                  Container(
                    height: 45.0,
                    child: TextFormField(
                      style: TextStyle(
                        fontFamily: 'Avenir-Medium',
                        fontSize: 12.0,
                        color: Colors.black,
                      ),
                      controller: _passwordController,
                      decoration: InputDecoration(
                        border: OutlineInputBorder(
                            borderRadius: const BorderRadius.all(
                              const Radius.circular(7.0),
                            ),
                            borderSide: BorderSide(
                              color: Colors.grey[200],
                              width: 0.0,
                            )),
                        labelText: 'password',
                      ),
                      obscureText: true,
                      autovalidate: true,
                      autocorrect: false,
                      validator: (_) {
                        return !state.isPasswordValid ? 'Invalid Password' : null;
                      },
                    ),
                  ),
                  Padding(
                    padding: EdgeInsets.symmetric(vertical: 20),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.stretch,
                      children: <Widget>[
                        LoginButton(
                          onPressed: _onFormSubmitted,

//                          isLoginButtonEnabled(state)
//                              ? _onFormSubmitted
//                              : null,
                        ),
                        GoogleLoginButton(),
                        AppleSignInButton(),
                        CreateAccountButton(userRepository: _userRepository),
                        ForgotPasswordButton()
                      ],
                    ),
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }

  @override
  void dispose() {
    _emailController.dispose();
    _passwordController.dispose();
    super.dispose();
  }

  void _onEmailChanged() {
    _loginBloc.add(
      EmailChanged(email: _emailController.text),
    );
  }

  void _onPasswordChanged() {
    _loginBloc.add(
      PasswordChanged(password: _passwordController.text),
    );
  }

  void _onFormSubmitted() {
    _loginBloc.add(
      LoginWithCredentialsPressed(
        email: _emailController.text,
        password: _passwordController.text,
      ),
    );
  }
}

the code for the second error above is as follows

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  BlocSupervisor.delegate = SimpleBlocDelegate();
  final UserRepository userRepository = UserRepository();
  runApp(
    BlocProvider(
      create: (context) => AuthenticationBloc(
        userRepository: userRepository,
      )..add(AppStarted()),
      child: App(userRepository: userRepository),
    ),
  );
}

错误图片

第二个错误

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

首先,BlocBuilder/BlocListener应该在对应的BlocProvider的scope中。

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

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);
        }
      },
    );
  }
}

因此,我们的 Screen Widget 应该在 ScreenBloc 的 Scope 中(作为它的子项)。 我们可以通过以下方式实现这一点:

BlocProvider<ScreenBloc>(
  create: (context) => ScreenBloc(),
  child: Screen(),
);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM