При проталкивании нового маршрута во Flutter с использованием библиотеки flutter_blo c контекст не работает так, как я ожидал - PullRequest
1 голос
/ 23 января 2020

Когда я захожу, используя форму в signin_content.dart, я перехожу на домашнюю страницу. И тогда я могу выйти из системы, потому что я не вывел sh на другой экран, поэтому он отлично работает.

Проблема в том, что когда я запускаю приложение, я вижу первую страницу signin_content.dart.

И затем, если я закажу sh для signup_content.dart и вернусь к signin_content.dart и попытаюсь войти, контекст в провайдере не работает.

BlocProvider.of<AuthenticationBloc>(context).add(LoggedIn()),

Я уже пробовал это решение, но мне нужно посмотреть, как найти код:

ссылка

Я пытался использовать провайдер как родитель для MaterialApp, но как?

Я использую эту библиотеку:

Вход в систему

main.dart

// imports ..

    class SimpleBlocDelegate extends BlocDelegate {
  @override
  void onEvent(Bloc bloc, Object event) {
    super.onEvent(bloc, event);
    print(event);
  }

  @override
  void onTransition(Bloc bloc, Transition transition) {
    super.onTransition(bloc, transition);
    print(transition);
  }

  @override
  void onError(Bloc bloc, Object error, StackTrace stacktrace) {
    super.onError(bloc, error, stacktrace);
    print(error);
  }
}

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

app.dart

// imports..

    class App extends StatelessWidget {
      final UserRepository userRepository;

  const App({
    Key key,
    @required this.userRepository,
  })  : assert(userRepository != null),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: fitnessTheme(),
      routes: Routes.appRoutes,
      home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
        builder: (context, state) {
          if (state is AuthenticationLoading) {
            return LoadingIndicator();
          }
          if (state is AuthenticationUnauthenticated) {
            return SigninScreen();
          }
          if (state is AuthenticationProfileInactive) {
            return WelcomeScreen();
          }
          if (state is AuthenticationAuthenticated) {
            return HomeScreen();
          }

          return Splash();
        },
      ),
    );
  }
}

signin_content.dart

//imports..

class SigninContent extends StatefulWidget {
  SigninContent({Key key}) : super(key: key);

  _SigninContentState createState() => _SigninContentState();
}

class _SigninContentState extends State<SigninContent> {
  SigninFormBloc _signinFormBloc;

  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  final UserProvider _userProvider = UserProvider();

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

  @override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;

    print(BlocProvider.of<AuthenticationBloc>(context).state);

    return BlocBuilder<SigninFormBloc, SigninFormState>(
      builder: (context, state) {
        if (state.formSubmittedSuccessfully) {
          final authData = {
            'email': _emailController.value.text,
            'password': _passwordController.value.text,
          };

          _userProvider.signin(
            data: authData,
            success: () =>
                BlocProvider.of<AuthenticationBloc>(context).add(LoggedIn()),
            error: (message) {
              Scaffold.of(context).showSnackBar(
                buildSnackbar(
                  message,
                  Colors.red[700],
                  Colors.white,
                  Duration(seconds: 2),
                ),
              );
            },
          );

          // _emailController.clear();
          // _passwordController.clear();
          _signinFormBloc.add(FormReset());
        }

        return Column(
          children: <Widget>[
            Expanded(
              child: SingleChildScrollView(
                child: Container(
                  margin: EdgeInsets.only(top: 16.0),
                  padding: EdgeInsets.only(
                    top: 29.0,
                    left: 14.0,
                    right: 14.0,
                  ),
                  child: Column(
                    children: <Widget>[
                      textInputWidget(
                        controller: _emailController,
                        labelText: "Email",
                        hintText: 'Enter a valid email',
                        autovalidate: state.email.isEmpty ? false : true,
                        validator: (_) {
                          return state.isEmailValid ? null : 'Invalid Email';
                        },
                      ),
                      SizedBox(height: 20.0),
                      textInputWidget(
                        controller: _passwordController,
                        labelText: "Password",
                        hintText: 'Enter a valid password',
                        obscureText: true,
                        autovalidate: state.password.isEmpty ? false : true,
                        validator: (_) {
                          return state.isPasswordValid
                              ? null
                              : 'Invalid Password';
                        },
                      ),
                      SizedBox(height: 20.0),
                      primaryButton(
                        caption: "sign in",
                        context: context,
                        submit: state.isFormValid ? _onSubmitPressed : null,
                      ),
                      SizedBox(height: 10.0),
                      Row(
                        children: <Widget>[
                          Text("Need an account?"),
                          FlatButton(
                            materialTapTargetSize:
                                MaterialTapTargetSize.shrinkWrap,
                            textColor: Color(0xFF32AEE2),
                            child: Text(
                              "Sign up",
                              style: TextStyle(
                                fontSize: 14.0,
                                fontFamily: "SF Pro Text",
                              ),
                            ),
                            onPressed: () => Navigator.pushReplacementNamed(
                              context,
                              SignupScreen.routeName,
                            ),
                          )
                        ],
                      ),
                      Container(
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.start,
                          children: <Widget>[
                            Text("Forgot your password?"),
                            FlatButton(
                              padding: EdgeInsets.all(0),
                              materialTapTargetSize:
                                  MaterialTapTargetSize.shrinkWrap,
                              textColor: Color(0xFF32AEE2),
                              child: Text(
                                "Reset",
                                style: TextStyle(
                                  fontSize: 14.0,
                                  fontFamily: "SF Pro Text",
                                ),
                              ),
                              onPressed: () => Navigator.pushReplacementNamed(
                                context,
                                PasswordResetScreen.routeName,
                              ),
                            )
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ],
        );
      },
    );
  }

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

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

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

  void _onSubmitPressed() {
    _signinFormBloc.add(FormSubmitted());
  }
}

signup_content. дротик

// imports..

class SignupContent extends StatefulWidget {
  SignupContent({Key key}) : super(key: key);

  _SignupContentState createState() => _SignupContentState();
}

class _SignupContentState extends State<SignupContent> {
  SignupFormBloc _signupFormBloc;


  final TextEditingController _nameController = TextEditingController();
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _dobController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();
  final TextEditingController _passwordConfirmationController =
      TextEditingController();

  final UserProvider _userProvider = UserProvider();

  @override
  void initState() {
    super.initState();
    _signupFormBloc = BlocProvider.of<SignupFormBloc>(context);
    _nameController.addListener(_onNameChanged);
    _emailController.addListener(_onEmailChanged);
    _dobController.addListener(_onDobChanged);
    _passwordController.addListener(_onPasswordChanged);
    _passwordConfirmationController.addListener(_onPasswordConfirmationChanged);
  }

  @override
  Widget build(BuildContext context) {
    final Size _size = MediaQuery.of(context).size;
    print(BlocProvider.of<AuthenticationBloc>(context).state);

    return BlocBuilder<SignupFormBloc, SignupFormState>(
      builder: (context, state) {
        if (state.formSubmittedSuccessfully) {
          final userData = {
            'name': _nameController.value.text,
            'email': _emailController.value.text,
            'password': _passwordController.value.text,
          };

          _userProvider.signup(
            userData: userData,
            success: () {
              BlocProvider.of<AuthenticationBloc>(context).add(
                SignedUp(
                  email: userData["email"],
                  password: userData["password"],
                ),
              );
            },
            error: (message) {
              Scaffold.of(context).showSnackBar(
                buildSnackbar(
                  message,
                  Colors.red[700],
                  Colors.white,
                  Duration(seconds: 2),
                ),
              );
            },
          );
          _signupFormBloc.add(FormReset());
        }

        return Column(
          children: <Widget>[
            authHeader(_size),
            Expanded(
              child: SingleChildScrollView(
                child: Container(
                  margin: EdgeInsets.only(top: 16.0),
                  padding: EdgeInsets.only(
                    top: 29.0,
                    left: 14.0,
                    right: 14.0,
                  ),
                  child: Column(
                    children: <Widget>[
                      textInputWidget(
                        controller: _nameController,
                        labelText: "Name",
                        hintText: 'Enter a valid name',
                        autovalidate: state.name.isEmpty ? false : true,
                        validator: (_) {
                          return state.isNameValid
                              ? null
                              : 'At least 6 characters long.';
                        },
                      ),
                      SizedBox(height: 20.0),
                      textInputWidget(
                        controller: _emailController,
                        labelText: "Email",
                        hintText: 'Enter a valid email',
                        autovalidate: state.email.isEmpty ? false : true,
                        validator: (_) {
                          return state.isEmailValid ? null : 'Invalid Email';
                        },
                      ),
                      SizedBox(height: 20.0),
                      textInputWidget(
                        controller: _passwordController,
                        labelText: "Password",
                        hintText: 'Enter a valid password',
                        obscureText: true,
                        autovalidate: state.password.isEmpty ? false : true,
                        validator: (_) {
                          return state.isPasswordValid
                              ? null
                              : 'Invalid Password';
                        },
                      ),
                      SizedBox(height: 20.0),
                      textInputWidget(
                        controller: _passwordConfirmationController,
                        labelText: "Password Confirmation",
                        hintText: 'Enter a valid password',
                        autovalidate:
                            state.passwordConfirmation.isEmpty ? false : true,
                        validator: (_) {
                          return state.isPasswordConfirmationValid
                              ? null
                              : 'Invalid Password';
                        },
                        obscureText: true,
                      ),
                      SizedBox(height: 20.0),
                      primaryButton(
                        caption: "sign up",
                        context: context,
                        submit: state.isFormValid ? _onSubmitPressed : null,
                      ),
                      SizedBox(height: 10.0),
                      Row(
                        children: <Widget>[
                          Text("Already Registered?"),
                          FlatButton(
                            materialTapTargetSize:
                                MaterialTapTargetSize.shrinkWrap,
                            textColor: Color(0xFF32AEE2),
                            child: Text(
                              "Sign in",
                              style: TextStyle(
                                fontSize: 14.0,
                                fontFamily: "SF Pro Text",
                              ),
                            ),
                            onPressed: () => Navigator.pushReplacementNamed(
                              context,
                              SigninScreen.routeName,
                            ),
                          )
                        ],
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ],
        );
      },
    );
  }

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


  void _onNameChanged() {
    _signupFormBloc.add(NameChanged(name: _nameController.text));
  }

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


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

  void _onPasswordConfirmationChanged() {
    _signupFormBloc.add(
      PasswordConfirmationChanged(
        password: _passwordController.text,
        passwordConfirmation: _passwordConfirmationController.text,
      ),
    );
  }

  void _onSubmitPressed() {
    _signupFormBloc.add(
      FormSubmitted(),
    );
  }

}

1 Ответ

2 голосов
/ 24 января 2020

Это потому, что в SignupContent вы заменяете дом вашего MaterialApp, поэтому BlocBuilder удаляется, а виджет не меняется даже при изменении состояния аутентификации.

Исправить Поэтому я рекомендую вам удалить BlocBuilder и использовать BlocListener на каждом экране, таким образом, вы можете без проблем использовать Navigator.

Так что измените main на

void main() {
  BlocSupervisor.delegate = SimpleBlocDelegate();

  final userRepository = UserRepository(); 
  final authenticationBloc = AuthenticationBloc(userRepository: userRepository);

  authenticationBloc
      .firstWhere((state) =>
          state is! AuthenticationUninitialized &&
          state is! AuthenticationLoading)
      .then((state) => runApp(App(
            authenticationBloc: authenticationBloc,
            home: state is AuthenticationUnauthenticated
                ? SigninScreen()
                : HomeScreen(),
          )));

  authenticationBloc.add(AppStarted());
}

Также измените ваш App виджет

class App extends StatelessWidget {
  const App({
    Key key,
    @required this.home,
    @required this.authenticationBloc,
  }) : super(key: key);

  final Widget home;
  final AuthenticationBloc authenticationBloc;

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: <BlocProvider>[
        BlocProvider<AuthenticationBloc>.value(value: authenticationBloc),
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: fitnessTheme(),
        routes: Routes.appRoutes,
        home: home,
      ),
    );
  }
}

Затем используйте BlocListener в ваших SigninScreen и SignupScreen для навигации и покажите диалог загрузки при состоянии аутентификации изменения.

class SigninScreen extends StatelessWidget {
  static const String routeName = "signin";

  const SigninScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    ScreenRatio.setScreenRatio(
      MediaQuery.of(context).size.height,
      MediaQuery.of(context).size.width,
    );
    return BlocProvider(
        create: (context) => SigninFormBloc(),
        child: BlocListener<AuthenticationBloc, AuthenticationState>(
          listener: (context, state) {
            if (state is AuthenticationLoading) {
              LoadingDialog.show(context);
            } else if (state is AuthenticationUnauthenticated) {
              LoadingDialog.hide(context);
            } else if (state is AuthenticationAuthenticated) {
              LoadingDialog.hide(context);
              Navigator.of(context).pushNamedAndRemoveUntil(
                HomeScreen.routeName, (route) => false);
            }
          },
          child: Scaffold(
            body: SafeArea(
              child: SigninContent(),
            ),
          ),
        ));
  }
}

class SignupScreen extends StatelessWidget {
  static const String routeName = "signup";

  const SignupScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    ScreenRatio.setScreenRatio(
        MediaQuery.of(context).size.height, MediaQuery.of(context).size.width);
    return BlocProvider(
        create: (context) => SigninFormBloc(),
        child: BlocListener<AuthenticationBloc, AuthenticationState>(
          listener: (context, state) {
            if (state is AuthenticationLoading) {
              LoadingDialog.show(context);
            } else if (state is AuthenticationUnauthenticated) {
              LoadingDialog.hide(context);
            } else if (state is AuthenticationAuthenticated) {
              LoadingDialog.hide(context);
              Navigator.of(context).pushNamedAndRemoveUntil(
                HomeScreen.routeName, (route) => false);
            }
          },
          child: Scaffold(
            body: SafeArea(
              child: SignupContent(),
            ),
          ),
        ));
  }
}

class LoadingDialog extends StatelessWidget {
  static void show(BuildContext context, {Key key}) {
    showDialog<void>(
      context: context,
      barrierDismissible: false,
      builder: (_) => LoadingDialog(key: key),
    );
  }

  static void hide(BuildContext context) {
    Navigator.pop(context);
  }

  LoadingDialog({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async => false,
      child: Center(
        child: Card(
          child: Container(
            width: 80,
            height: 80,
            padding: EdgeInsets.all(12.0),
            child: CircularProgressIndicator(),
          ),
        ),
      ),
    );
  }
}

И, наконец, в вашем HomeScreen используйте BlocListener для навигации, когда пользователь вышел из системы

return BlocListener<AuthenticationBloc, AuthenticationState>(
      listener: (context, state) {
        if (state is AuthenticationUnauthenticated) {
          Navigator.of(context).pushNamedAndRemoveUntil(
              SigninScreen.routeName, (route) => false);
        }
      },
      child: Scaffold(
        body: Center(
          child: RaisedButton(
            onPressed: () {
              BlocProvider.of<AuthenticationBloc>(context).add(LoggedOut());
            },
            child: Text("Sign out"),
          ),
        ),
      ),
    );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...