Проблемы с authStateChanges в Flutter

#firebase #flutter #dart #firebase-authentication

#firebase #flutter #dart #firebase-аутентификация

Вопрос:

Всякий раз, когда пользователь закрывает приложение, он должен повторно войти в систему. Я посмотрел и попытался реализовать authStateChanges. Но все же мое приложение по-прежнему заставляет пользователей снова входить в систему после закрытия приложения. В классе App вы можете видеть, что я пытался выполнить именно authStateChange, но, к сожалению, ничего не происходит.

 Future<void> main() async {


WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(App());
}

// Firebase Auth Instance
FirebaseAuth auth = FirebaseAuth.instance;

class MyApp extends StatelessWidget {
  final Future<FirebaseApp> _initialization = Firebase.initializeApp();



// This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
  title: 'Tanbo',
  theme: ThemeData(
    primarySwatch: Colors.blue,
  ),
  home: LoginPage(),
);


}
}

// This is the main root screen of the Tanbo app
class App extends StatelessWidget {
  final Future<FirebaseApp> _initialization = Firebase.initializeApp();



@override
  Widget build(BuildContext context) {
    return FutureBuilder(
      // Initialize FlutterFire
      future: _initialization,
  

builder: (context, snapshot) {
    final user = FirebaseAuth.instance.authStateChanges().listen((User user) {
      if (user == null) {
        print('User signed out');
      } else {
        print('User signed in');
      }
    });

    // Check for errors
    if (snapshot.hasError) {
      return ErrorHandler();
    }

    // Once complete, show your application
    if (snapshot.connectionState == ConnectionState.done) {
      // If the user isn't logged in, we will be sent to sign up page.
      if (user != null) {
        return MyApp();
      } else {
        // If the user is logged in, TabHandler will be shown.
        return TabHandler();
      }
    }

    // Otherwise, show something whilst waiting for initialization to complete
    return LoadingHandler();
  },
);


 }
}
  

Комментарии:

1. Вы должны реагировать на изменения в состоянии пользователя внутри обратного вызова, который вы передаете listen() , а не сразу после настройки прослушивателя. Пользователь снова входит в систему только по истечении некоторого времени после добавления слушателя, и только ваш слушатель будет знать об этом изменении.

Ответ №1:

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

Вот как вы это исправляете. А также создайте систему аутентификации, в которой вы можете получить идентификатор зарегистрированного пользователя в любой точке приложения.

Что вам нужно:

  1. Зависимость от поставщика в зависимости от версии. Желательно последнюю
  2. Улыбка — перестаньте хмуриться. Вы собираетесь решить свою проблему
  3. Зависимость от аутентификации Firebase. В зависимости от версии. Я собираюсь дать вам исправление для новейшей версии и для явно более старых версий.

Шаг 0: добавьте необходимые зависимости и запустите flutter pub get. Это не проблема.

Шаг 1: создайте auth_services класс: код приведен ниже

Для более старых версий firebase auth :

 import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';

class AuthService {
  final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
  final GoogleSignIn _googleSignIn = GoogleSignIn();

  Stream<String> get onAuthStateChanged =>
      _firebaseAuth.onAuthStateChanged.map(
            (FirebaseUser user) => user?.uid,
      );

  // GET UID
  Future<String> getCurrentUID() async {
    return (await _firebaseAuth.currentUser()).uid;
  }
}
  

Для более новых версий зависимости аутентификации firebase:

 class AuthService {
  final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
  final GoogleSignIn _googleSignIn = GoogleSignIn();

  Stream<User> get onAuthStateChanged => _firebaseAuth.authStateChanges();

  // GET UID
  Future<String> getCurrentUID() async {
    return _firebaseAuth.currentUser.uid;
  }
}
  

Объяснение: я создал этот класс для обработки всех функций аутентификации. Я создаю функцию с именем onAuthStateChanged, которая возвращает поток типа User (идентификаторы для более старых версий firebase auth), и я собираюсь прослушать этот поток, чтобы узнать, вошел ли пользователь в систему или нет.

Шаг 2: Создайте поставщика аутентификации, который будет охватывать все приложение и позволит получить наш идентификатор пользователя в любом месте приложения.

Создайте файл с именем auth_provider.dart. Код приведен ниже.

 import 'package:flutter/material.dart';
import 'auth_service.dart';

class Provider extends InheritedWidget {
  final AuthService auth;
  Provider({
    Key key,
    Widget child,
    this.auth,
  }) : super(key: key, child: child);

  @override
  bool updateShouldNotify(InheritedWidget oldWiddget) {
    return true;
  }

  static Provider of(BuildContext context) =>
      (context.dependOnInheritedWidgetOfExactType<Provider>());
}
  

Следующий шаг — обернуть все приложение в этот виджет поставщика и установить домашний контроллер в качестве домашней страницы.

Шаг 3: В файле main.dart или в любом другом месте создайте класс с именем HomeController, который будет обрабатывать состояние входа в систему, и установите домашний контроллер в качестве назначенной домашней страницы.

ПРИМЕЧАНИЕ: я установил контейнер черного цвета, который будет отображаться при загрузке приложения, чтобы определить, вошел ли пользователь в систему или нет. Это довольно быстрый процесс, но если вы хотите, вы можете настроить его как контейнер цвета темы вашего приложения. Вы даже можете установить его в качестве заставки. (Пожалуйста, обратите внимание. Этот контейнер отображается не более 1 секунды. Из моего опыта)

Код приведен ниже: импортируйте все необходимые вещи

 void main() {
 //WidgetsFlutterBinding.ensureInitialized();
 // await Firebase.initializeApp();
//only add these if you're on the latest firebase
  runApp(MyApp());
}

 class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider(
      auth: AuthService(),
      child: MaterialApp(
            title: 'Dreamora',
            theme: ThemeData(
              // fontFamily: "Montserrat",
              brightness: Brightness.light,
              inputDecorationTheme: InputDecorationTheme(
                contentPadding:
                    EdgeInsets.only(top: 10, bottom: 10, left: 10, right: 10),
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(5.0),
                ),
              ),
              primarySwatch: Colors.purple,
              visualDensity: VisualDensity.adaptivePlatformDensity,
            ),
            home: HomeController(),
          );
        },
      ),
    ),}
//(I think i have messed up the brackets here, but, you get the 
//gist)

class HomeController extends StatelessWidget {
  const HomeController({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final AuthService auth = Provider.of(context).auth;

    return StreamBuilder(
      stream: auth.onAuthStateChanged,
      builder: (context, AsyncSnapshot<String> snapshot) {
        if (snapshot.connectionState == ConnectionState.active) {
          final bool signedIn = snapshot.hasData;
          return signedIn ? DashBoard() : FirstView();
        }
        return Container(
          color: Colors.black,
        );
      },
    );
  }
}
  

Объяснение: Домашний контроллер — это просто конструктор steam, который прослушивает множество внесенных нами изменений аутентификации. Если есть какие-либо данные, пользователь входит в систему. Если нет, он вышел из системы. Довольно просто. Между определением состояния пользователя, вошедшего в систему, существует задержка в 1 секунду. Су, за это время я возвращаю черный контейнер. Пользователь увидит, что экран становится черным примерно на секунду после открытия приложения, а затем бум. Домашняя страница

ВАЖНО: вы должны обернуть все свое приложение поставщиком. Это важно. Не забывайте об этом.

КАК ПОЛУЧИТЬ ИДЕНТИФИКАТОР ПОЛЬЗОВАТЕЛЯ В ЛЮБОЙ ТОЧКЕ ПРИЛОЖЕНИЯ

 Provider.of(context).auth.getCurrentUID()
  

Вот так. Наслаждайтесь

[ПРАВИТЬ]Как выразился Л. Гангеми, новая версия Firebase Auth возвращает поток пользователей. Итак, отредактируйте код домашнего контроллера, чтобы

 builder: (context, AsyncSnapshot<User> snapshot) {
  

Комментарии:

1. Ваш ответ был очень полезен. Я просто хотел сказать, что в новейшей версии ваш код возвращает пользователя с потоком, поэтому AsyncSnaphot в StreamBuilder должен быть таким: AsyncSnapshot<Пользователь>

2. Хороший ответ, также используйте ? для User и других, который показывает нулевую ошибку

3. @Simeon Я знаю, что это немного устарело, но я всегда задавался вопросом, зачем использовать StreamBuilder вместо того, чтобы просто использовать FutureBuilder? Разве он не проверяет только один раз, когда отображается экран, а затем показывает соответствующий экран?

4. Хорошо.. Изменения состояния auth пакета auth предоставляют вам доступ к потоку данных, которые символизируют изменения статистики аутентификации пользователя. Например, вход в систему, выход из системы и т.д. Вы не можете использовать поток в сборщике future. По крайней мере, нелегко. Кроме того, конструктор потоков реагирует в режиме реального времени на изменения в потоке. То же самое делает будущий разработчик. Нам нужно что-то, что меняется в тот момент, когда кто-то выходит из системы. Следовательно .., stream builder

Ответ №2:

Я считаю, что это не работает из-за этой проблемы — https://github.com/FirebaseExtended/flutterfire/issues/3356

Решается путем понижения рейтинга firebase-app.js и firebase-auth.js версии до 7.20.0

и замена метода authStateChanges() на userChanges()

  FirebaseAuth.instance
     .userChanges()
     .listen((User user) {
         if (user == null) {
             print('User is currently signed out!');
         } else {
             print('User is signed in!');
         }
});
  

Ответ №3:

Использовать

 FirebaseAuth.instance
  .authStateChanges()
  .listen((User user) {
    if (user == null) {
      print('User is currently signed out!');
    } else {
      print('User is signed in!');
    }
  });
  

Комментарии:

1. Хотя этот код может дать решение вопроса, лучше добавить контекст относительно того, почему / как это работает. Это может помочь будущим пользователям учиться и применять эти знания к своему собственному коду. Вы также, вероятно, получите положительные отзывы от пользователей в виде положительных голосов, когда будет объяснен код.

2. Где было бы лучшее место для размещения этого кода?