如何正确在Flutter应用程序中使用BlocListener和BlocProvider

8
我在我的Flutter应用程序中使用了flutter_bloc 4.0.0,我使用了Felix Angelov( https://medium.com / flutter-community / firebase-login-with-flutter-bloc-47455e6047b0 )的示例来实现使用bloc模式进行登录流程。 它一直运行良好,但是在我更新Flutter并稍后检查我的代码后,我发现了一系列错误。 我不明白为什么它们出现,因为上周一切都很好。 在小部件的build方法中实现bloc突然变得错误。 我收到以下错误: 1."‘由于其返回类型为widget,因此无法从build方法返回BlocListener的值类型" 2."‘由于其返回类型为widget,因此无法从build方法返回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),
    ),
  );
}

image for the error

the second error

1个回答

25

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

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

你应该在你的Stateless/Stateful部件的build方法中返回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);
        }
      },
    );
  }
}
所以,我们的Screen Widget 应该在 ScreenBloc 的范围内(作为其子元素)。我们可以通过以下方式实现:
BlocProvider<ScreenBloc>(
  create: (context) => ScreenBloc(),
  child: Screen(),
);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接