Проблема Flutter с аутентификацией по электронной почте / паролю - PullRequest
0 голосов
/ 03 августа 2020



Я новичок во Flutter, и я пытаюсь создать систему аутентификации с логином и регистрацией.
Я следил за онлайн-руководством, и все работает нормально. Но мой процесс аутентификации немного отличается, и когда я добавляю дополнительный шаг, экраны входа или регистрации НЕ перенаправляют на главную целевую страницу.

Это код из учебника, который работает нормально . Вы заметите, что при запуске приложения первым появляется экран входа в систему, который вызывается Wrapper.dart через ЗАЯВЛЕНИЕ ЕСЛИ.

Здесь файлы из учебника:
учебник main.dart

void main() {
  runApp(Main());
}

class Main extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamProvider<User>.value(
        value: AuthService().user,
        child: MaterialApp(
          home: Wrapper(),
          debugShowCheckedModeBanner: false,
        )
      );
  }
}

учебник-оболочка.dart

class Wrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final user = Provider.of<User>(context);

    // return either Home or Auth widget
    if (user == null) {
      return Authenticate();
    } else {
      return Home();
    }
  }
}

учебный файл Authenticate.dart

class Authenticate extends StatefulWidget {
  @override
  _AuthenticateState createState() => _AuthenticateState();
}

class _AuthenticateState extends State<Authenticate> {
  bool showSignIn = true;
  void toggleView() {
    setState(() => showSignIn = !showSignIn);
  }

  @override
  Widget build(BuildContext context) {
    if (showSignIn) {
      return SignIn(toggleView: toggleView);
    } else {
      return Register(toggleView: toggleView);
    }
  }
}

учебник signIn.dart

class SignIn extends StatefulWidget {
  final Function toggleView;
  SignIn({this.toggleView});

  @override
  _SignInState createState() => _SignInState();
}

class _SignInState extends State<SignIn> {
  final AuthService _auth = AuthService();
  final _formKey = GlobalKey<FormState>();
  bool loading = false;

  // text Field State
  String email = '';
  String password = '';
  String error = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
            backgroundColor: Colors.brown[400],
            elevation: 0.0,
            title: Text('Sign in'),
            actions: <Widget>[
              FlatButton.icon(
                icon: Icon(Icons.person),
                label: Text('Register'),
                onPressed: () {
                  widget.toggleView();
                },
              )
            ]),
        body: Container(
            padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 50.0),
            child: Form(
                key: _formKey,
                child: Column(children: <Widget>[
                  SizedBox(height: 20.0),
                  TextFormField(
                    decoration: textInputDecoration.copyWith(hintText: 'Email'),
                    validator: (val) => val.isEmpty ? 'Enter an email' : null,
                    onChanged: (val) {
                      setState(() => email = val);
                    },
                  ),
                  SizedBox(height: 20.0),
                  TextFormField(
                    obscureText: true,
                    decoration:
                        textInputDecoration.copyWith(hintText: 'Password'),
                    validator: (val) => val.length < 6
                        ? 'Password is too short. Min 6 characters long'
                        : null,
                    onChanged: (val) {
                      setState(() => password = val);
                    },
                  ),
                  SizedBox(height: 20.0),
                  RaisedButton(
                    color: Colors.pink[400],
                    child: Text(
                      'Sign in',
                      style: TextStyle(color: Colors.white),
                    ),
                    onPressed: () async {
                      if (_formKey.currentState.validate()) {
                        setState(() => loading = true);

                        dynamic result = await _auth.signInWithEmailAndPassword(
                            email, password);
                        if (result == null) {
                          setState(() {
                            error =
                              'These credentials are invalid. Please check them and try again!';
                            loading = false;
                          });
                        }
                      }
                    },
                  ),
                ]))
            ));
  }
}

Файл Registration.dart похож на SignIn, поэтому я не буду показывать его здесь.
последний файл, чтобы сделать заметку, это служба под названием AuthService.dart

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  // Create user object based on FirebaseUser
  User _userFromFirebaseUser(FirebaseUser user) {
    return user != null ? User(uid: user.uid) : null;
  }

  // Auth change user stream
  Stream<User> get user {
    return _auth.onAuthStateChanged.map(
        _userFromFirebaseUser); // This is the equivalent of .map((FirebaseUser user) => _userFromFirebaseUser(user));
  }

  // SignIn with Email & Password
  Future signInWithEmailAndPassword(String email, String password) async {
    try {
      AuthResult result = await _auth.signInWithEmailAndPassword(
          email: email, password: password);
      FirebaseUser user = result.user;
      return _userFromFirebaseUser(user);
    } catch (e) {
      print(e.toString());
      return null;
    }
  }

}

Позвольте мне объяснить, как работает вышеуказанный поток
Когда пользователь запускает приложение, main.dart файл вызывает wrapper.dart, который проверяет, является ли пользователь нулевым или существует.
Если User=null, это означает, что он НЕ вошел в систему и вызывает Authenticate.dart, который по умолчанию открывает экран SignIn.
Если User != null, это означает, что пользователь вошел в систему и вызывает экран Home.dart.

****** МОЯ ПРОБЛЕМА *****
Теперь, в моем потоке (см. Ниже), Wrapper вызывает StartAuthButtons.dart, когда User=null.
The StartAuthButtons имеет несколько кнопок для аутентификации пользователя различными методами, например Google, Facebook, Apple и по электронной почте / паролю.

MY Wrapper.dart :

class Wrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final user = Provider.of<User>(context);

    if (user == null) {
      return StartAuthButtons();
      //return Authenticate(); <-- this was before
    } else {
      return Home();
    }
  }
}

Когда пользователь нажимает «Войти с помощью электронной почты и пароля», я использую Flutter от Navigate до go на странице входа. Это дополнительный шаг, который я делаю иначе, чем в учебнике. (пожалуйста, проверьте мои комментарии внутри кода ниже между звездочками)
My StartAuthButtons.dart

class StartAuthButtons extends StatefulWidget {
  @override
  _StartAuthButtonsState createState() => _StartAuthButtonsState();
}

class _StartAuthButtonsState extends State<StartAuthButtons> {
  final AuthService _auth = AuthService();

  @override
  Widget build(BuildContext context) {
    return Stack(children: [
            AuthLayout(),
            Scaffold(
                backgroundColor: Colors.transparent,
                appBar: AppBar(
                  title: Center(child: Text('Welcome')),
                  backgroundColor: Colors.transparent,
                ),
                body: SingleChildScrollView(
                  child: ConstrainedBox(
                    constraints: BoxConstraints(),
                    child: Center(
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.start,
                        children: <Widget>[

                          //Email & Password SignIn
                          Padding(
                            padding: EdgeInsets.all(8.0), child: new MaterialButton(
                              child: SignInEmailPasswordBtn(),
                              onPressed: () {
                                ****** I THINK THE ISSUE IS HERE *********
                                Navigator.push(
                                  context,
                                  MaterialPageRoute(builder: (context) => Authenticate()),
                                );

                                *** As you can see above, here is where I call Authenticate() ***
                                ****** But by adding this extra step to reach the SignIn page, when I signin it doesn't redirect to the Home once signed in. It stays on the SignIn screen. If I hard refresh the app, the system is logged in **********

                                
                              }
                            )
                          )
                        ]
                      )
                    )
                  )
            ))]);
    }
  }

Как только я использовал виджет Navigate для go в Страница SignIn, мне кажется, что я не могу перейти на go к экрану Home, когда я успешно вошел в систему.
Аутентификация работает нормально, потому что, если я обновлю sh приложение, оно показывает мне экран Home как зарегистрированный пользователь.

Кажется, что onPress для аутентификации, ЗАЯВЛЕНИЕ ЕСЛИ, которое выбирает между Home и StartAuthButton, не получить срабатывание.

Любая помощь очень ценится. Извините за длинный сценарий. Джо

1 Ответ

0 голосов
/ 04 августа 2020

Спасибо @Unbreachable за предложение посмотреть статью для pop & pu sh.

Я внес простое изменение в виджеты SignIn и Register на обеих кнопках отправки при нажатии .

Код до:

onPressed: () async {
   if (_formKey.currentState.validate()) {
     setState(() => loading = true);
                                          
    dynamic result = await _auth.registerWithEmailAndPassword(email, password);

    if (result == null) {
      setState(() {
          error = 'Email or password are invalid.';
          loading = false;
      });
    }
  }
},

и после:

onPressed: () async {
   if (_formKey.currentState.validate()) {
     setState(() => loading = true);
                                          
    dynamic result = await _auth.registerWithEmailAndPassword(email, password);

    if (result == null) {
      setState(() {
          error = 'Email or password are invalid.';
          loading = false;
      });
    } else {
      Navigator.of(context).pop(); <-- here the change. 
      loading = false; <-- here the change.
    }
  }
},

Теперь загрузка работает нормально, и когда я исчезаю, я вижу виджет home.

...