Flutter StreamProvider не обновляется после вызова pushReplacement

У меня есть приложение flutter, которое реализует аутентификацию firebase. После выполнения этого руководства мой файл main.dart выглядит так: это:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamProvider<User>.value(
      catchError: (_, err) => null,
      value: AuthService().user,
      child: MaterialApp(
        home: Wrapper(),
      ),
    );
  }
}

Где value: AuthService (). User возвращается в виде потока следующей функцией get:

// auth change user stream
  Stream<User> get user {
    return _auth.onAuthStateChanged.map(
      (FirebaseUser user) => _userFromFirebaseUser(user),
    );
  }

  User _userFromFirebaseUser(FirebaseUser user) {
    
    return user != null ? User(uid: user.uid) : null;
  }

А виджет Wrapper () выглядит так:

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

    if (user == null) {
      print("user is null, should show Authenticate()");
      return Authenticate();
    } else {
      return Home();
    }
  }
}

Этот код работает, когда:

  1. Вход из виджета Authenticate ()
  2. выход из главного экрана ().

Однако на главном экране () навигация выглядит так:

  • Home()
    • SecondPage()
      • ThirdPage()

Я использую Navigator.push () при движении вперед (т.е. от SecondPage () к ThirdPage ()). Затем я обернул скаффолды вторичной и третьей страницы в WillPopScope, где onWillPop вызывает Navigator.pushReplacement () для перехода назад.

(Примечание: я сделал это, потому что эти страницы читают и записывают данные из firebase firestore, а Navigator.pop () не вызывает повторную визуализацию. Это означает, что пользователь может что-то обновить на ThirdPage ( ), который изменяет значение в firestore, но когда я затем вызываю Navigator.pop (), он не обновляет SecondPage () новыми данными.)

Моя проблема в том, что что-то в Navigator.pushReplacement () при переходе со страницы SecondPage () на страницу Home () вызывает проблему, так что, когда пользователь затем выходит из системы, на экране не отображается страница Authenticate ().

Я поместил print("user is null, should show Authenticate()") вызов в Wrapper (), чтобы убедиться, что if (user == null) имеет значение true, когда пользователь нажимает кнопку выхода. Он печатает, но по-прежнему не отображает экран Authenticate ().

Я знаю, что это как-то связано с pushReplacement (), так как выход из системы напрямую из Home () без правильной навигации по функциям (т.е. показана страница Authenticate ()). Любая помощь приветствуется.


person Iain McHugh    schedule 06.07.2020    source источник


Ответы (2)


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

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

Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (context)=>
Wrapper()), (_) => false );

Я сделал оператор if-else на странице signIn, и когда пользователь вернул ненулевое значение, я снова вызвал Wrapper(), и это помогло.

person Saurabh Kumar    schedule 08.02.2021

Я столкнулся с той же проблемой, и я решил явно вернуться к Wrapper, когда пользователь выходит из системы с чем-то вроде этого:

  void signOut(BuildContext context) {
    Auth().handleSignOut();
    Navigator.pushAndRemoveUntil(
        context,
        MaterialPageRoute(builder: (context) => Wrapper()),
            (_) => false
    );
  }

Я все еще слушаю поток в Провайдере, чтобы аннулировать данные пользователя по соображениям безопасности, но я не полагаюсь на этот слушатель, чтобы вернуться к начальному экрану.

 void initAuthListener(){
    userStream = Auth().user;
    userStream.listen((data) {
      if(data==null){
        voidUser();
      }else{
        setUser(data);
      }
    }, onDone: () {
      print("Task Done");
    }, onError: (error) {
      print("Some Error");
    });
  }

Сказав, что я был бы счастлив, если бы кто-нибудь мог пролить свет на то, почему Wrapper не перестраивается в нашем первоначальном случае.

Еще одна вещь, вы упомянули, Я сделал это, потому что эти страницы читают и записывают данные из firebase firestore, а Navigator.pop () не вызывает повторную визуализацию. Я бы посоветовал вам использовать Streams для извлечения данных из firestore и Streambuilders, чтобы отображать их, как описано в person Francois Souterelle    schedule 19.11.2020