Flutter:StreamBuilder 快照——无数据

Posted

技术标签:

【中文标题】Flutter:StreamBuilder 快照——无数据【英文标题】:Flutter: StreamBuilder Snapshot -- No Data 【发布时间】:2020-08-23 12:06:11 【问题描述】:

我刚刚学习 Flutter,并尝试使用 StreamBuilder 在用户注销时显示登录/注册页面,或者在用户登录时显示个人资料页面。我的代码如下:

认证服务:

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

class AuthUser 
  AuthUser(@required this.uid, @required this.email);
  final String uid;
  final String email;


abstract class AuthBase 
  Future<AuthUser> currentUser();
  Future<AuthUser> signIn(String email, String pw);
  Future<AuthUser> registerUser(String email, String pw);
  Stream<AuthUser> get onAuthStateChanged;
  Future<void> signOut();


class Auth implements AuthBase 
  final _firebaseAuth = FirebaseAuth.instance;

  AuthUser _userFromFirebase(FirebaseUser user) 
    if (user != null) 
      return AuthUser(uid: user.uid, email: user.email);
     else 
      return null;
    
  

  @override
  Stream<AuthUser> get onAuthStateChanged 
    return _firebaseAuth.onAuthStateChanged.map(_userFromFirebase);
  

  @override
  Future<AuthUser> currentUser() async 
    final user = await _firebaseAuth.currentUser();
    return _userFromFirebase(user);
  

  @override
  Future<AuthUser> signIn(String email, String pw) async 
    final authResult = await _firebaseAuth.signInWithEmailAndPassword(email: email, password: pw);
    return _userFromFirebase(authResult.user);
  

  @override
  Future<AuthUser> registerUser(String email, String pw) async 
    final authResult = await _firebaseAuth.createUserWithEmailAndPassword(email: email, password: pw);
    return _userFromFirebase(authResult.user);
  

  @override
  Future<void> signOut() async 
    await _firebaseAuth.signOut();
  


StreamBuilder:

class WelcomeScreen extends StatelessWidget 
  WelcomeScreen(@required this.auth);
  static const String id = '/';
  final AuthBase auth;

  @override
  Widget build(BuildContext context) 
    return StreamBuilder<AuthUser>(
      stream: auth.onAuthStateChanged,
      builder: (context, snapshot) 
        if (snapshot.hasData) 
          AuthUser user = snapshot.data;
          if (user == null) 
            return displayLoginOrRegPage(context);
           else 
            return ProjectScreen(
              user: user,
              auth: auth,
            );
          
         else 
          return Scaffold(
            body: Center(
              child: CircularProgressIndicator(),
            ),
          );
        
      ,
    );
  

据我了解,流一旦初始化就会开始发出“null”,并且会继续这样做,直到它触发 Auth 状态更改...

但快照不断报告“无数据”,因此我的代码卡在 CircularProgressIndicator 上。

顺便说一句,如果我显示登录屏幕代替进度指示器,代码就可以工作。所以我显然不了解整个流初始化过程。

有人可以向我解释我在哪里出错了吗?提前一百万谢谢。

【问题讨论】:

【参考方案1】:

正如您所提到的,当stream 初始化时它会发出null,但是当用户没有登录时,它仍然会发出null,stream 认为没有数据即null 这就是错误的原因.

您可以使用Streambuilder的连接状态来区分无用户null和初始化后的null。

希望下面的代码对你有所帮助。

  if (snapshot.connectionState == ConnectionState.active) 
        if (snapshot.data == null) 
               return displayLoginOrRegPage(context);
          else 
            AuthUser user = snapshot.data;
               return ProjectScreen(
                    user: user,
                    auth: auth,
                ); 
           
     else 
         return Scaffold(
             body: Center(
             child: CircularProgressIndicator(),
            ),
         );
    

【讨论】:

这是一个很好的提示。我现在不在我的开发机器上,但我明天一定会检查一下。我确定这是我正在寻找的答案,当我确认时我会这样标记。提前谢谢!【参考方案2】:

尝试将 WelcomeScreen 更改为完整的 Widget。

【讨论】:

在我重构它以使用 StreamBuilder 之前,我的代码是一个有状态的小部件。它现在没有状态逻辑,所以我努力摆脱它。我很确定@viren-v-varasadiya 有正确的答案。不过谢谢!【参考方案3】:

根据@VirenVVarasadiya,这绝对是一个检查 ConnectionState 的案例。这是最终的工作代码。谢谢!

  @override
  Widget build(BuildContext context) 
    return StreamBuilder<AuthUser>(
      stream: auth.onAuthStateChanged,
      builder: (context, snapshot) 
        if (snapshot.connectionState == ConnectionState.active) 
          if (snapshot.data != null) 
            AuthUser user = snapshot.data;
            return ProjectScreen(
              user: user,
              auth: auth,
            );
           else 
            return displayLoginOrRegPage(context);
          
         else 
          return Scaffold(
            body: Center(
              child: CircularProgressIndicator(),
            ),
          );
        
      ,
    );
  

【讨论】:

以上是关于Flutter:StreamBuilder 快照——无数据的主要内容,如果未能解决你的问题,请参考以下文章

Flutter:将firestore快照转换为streambuilder中的列表

在 Flutter 中从 Firebase FireCloud 接收数据时,StreamBuilder 中的快照代码不会被执行

Flutter StreamBuilder中使用的Firestore快照在数据更改时永远不会更新

StreamBuilder 为每次更新构建两次快照

从 mysql 获取数据到 StreamBuilder Flutter

StreamBuilder总是在flutter中的print(snapshot.hasData)中返回false