【问题标题】:Flutter How to handle Stream and Future return errorFlutter 如何处理 Stream 和 Future 返回错误
【发布时间】:2022-01-19 13:42:36
【问题描述】:

我正在尝试流式传输我的 firebase firestore 字段。这是我的数据库中的代码。它在按钮中工作,我可以打印我想要的。但实际上我想使用 StreamBuilder 在我的主页中显示带有小部件的数据。

  getYukListFromDB() async {
    _firebaseFirestore
        .collection('customer')
        .get()
        .then((QuerySnapshot querySnapshot) {
      for (var docA in querySnapshot.docs) {
        debugPrint("shipment altındaki docs idsi = " + docA.id);
        _firebaseFirestore
            .collection('customer')
            .doc(docA.id)
            .collection('myYuks')
            .get()
            .then((QuerySnapshot querySnapshot) {
          for (var docB in querySnapshot.docs) {
            debugPrint("myYuk altındaki docs idsi = " + docB.id);
            _firebaseFirestore
                .collection('customer')
                .doc(docA.id)
                .collection('myYuks')
                .doc(docB.id)
                .get()
                .then((DocumentSnapshot documentSnapshot) {
              Map<String, dynamic> mapData =
                  documentSnapshot.data()! as Map<String, dynamic>;
              if (documentSnapshot.exists) {
                debugPrint("icerik =  ${mapData['icerik']}");
                debugPrint('doc var');
              } else {
                debugPrint('doc yok');
              }
            });
          }
        });
      }
    });

  
  }

当我在流中尝试这个时,它给了我一个错误。

Stream<List<YukModel>> yeniYukStream(String uid) { // ***error here***
 _firebaseFirestore
        .collection('customer')
        .get()
        .then((QuerySnapshot querySnapshot) {
      for (var doc1 in querySnapshot.docs) {
        debugPrint("shipment altındaki docs idsi = " + doc1.id);
        return _firebaseFirestore
            .collection("customer")
            .doc(doc1.id)
            .collection('myYuks')
            .get()
            .then((QuerySnapshot querySnapshot) {
          for (var doc2 in querySnapshot.docs) {
            debugPrint("myYuk altındaki docs idsi = " + doc2.id);
            _firebaseFirestore
                .collection('customer')
                .doc(doc2.id)
                .collection('myYuks')
                .orderBy('createTime', descending: true)
                .snapshots()
                .map((QuerySnapshot querySnapshot) {
              List<YukModel> retVal = <YukModel>[];
              for (var lastData in querySnapshot.docs) {
                retVal.add(YukModel.fromDocumentSnapshot(lastData));
              }
              return retVal;
            });
          }
        });
      }
    });
  }

主体可能正常完成,导致返回“null”,但返回类型可能是不可为空的类型。 尝试在末尾添加 return 或 throw 语句。

而且我知道它应该是未来。让我们试试吧。

Future<Stream<List<YukModel>>> yeniYukStream(String uid) async{
 return _firebaseFirestore
        .collection('customer')
        .get()
        .then((QuerySnapshot querySnapshot) { // ****error here****
      for (var doc1 in querySnapshot.docs) {
        debugPrint("shipment altındaki docs idsi = " + doc1.id);
        return _firebaseFirestore
            .collection("customer")
            .doc(doc1.id)
            .collection('myYuks')
            .get()
            .then((QuerySnapshot querySnapshot) { // ****error here****
          for (var doc2 in querySnapshot.docs) {
            debugPrint("myYuk altındaki docs idsi = " + doc2.id);
            _firebaseFirestore
                .collection('customer')
                .doc(doc2.id)
                .collection('myYuks')
                .orderBy('createTime', descending: true)
                .snapshots()
                .map((QuerySnapshot querySnapshot) { 
              List<YukModel> retVal = <YukModel>[];
              for (var lastData in querySnapshot.docs) {
                retVal.add(YukModel.fromDocumentSnapshot(lastData));
              }
              return retVal;
            });
          }
        });
      }
    });
  }

****错误****行是这样的:

主体可能正常完成,导致返回“null”,但返回类型可能是不可为空的类型。 尝试在末尾添加 return 或 throw 语句。

这是我的 YukModel.dart 文件;

class YukModel {
  String? yukID;
  String? yukBaslik;
  String? icerik;
  int? agirlik;
  Timestamp? createTime;
  String? aracTipi;
  bool? onayDurumu;

  YukModel(
      {this.yukID,
      this.yukBaslik,
      this.icerik,
      this.agirlik,
      this.createTime,
      this.aracTipi,
      this.onayDurumu});


  YukModel.fromDocumentSnapshot(DocumentSnapshot documentSnapshot) {
    yukID = documentSnapshot.id;
    yukBaslik = documentSnapshot.get('yukBaslik');
    icerik = documentSnapshot.get('icerik');
    agirlik = documentSnapshot.get('agirlik');
    createTime = documentSnapshot.get('createTime');
    aracTipi = documentSnapshot.get('aracTipi');
    onayDurumu = documentSnapshot.get('onayDurumu');
  }
}

我该怎么办?另外,我正在使用 GetX 包进行状态管理。

我现在的解决方案

这是我的解决方案。它对我有用。此外,您可以使用此代码访问您的子集合。

在 HomePage 中,我添加了一个 StreamBuilder:

StreamBuilder(
        stream: FirebaseFirestore.instance
            .collection("customer")
            .doc(authController.doneUser!.uid) // you can use your uid
            .collection("myYuks")
            .snapshots(),
        builder: (BuildContext context,
            AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
          if (snapshot.hasData && snapshot.data != null) {
            if (snapshot.data!.docs.isNotEmpty) {
              return ListView.builder( 
                itemBuilder: (context, int index) {
                  Map<String, dynamic> docData =
                      snapshot.data!.docs[index].data();

                  if (docData.isEmpty) {
                    return const Center(child: Text("Data empty"));
                  }

//these are my fields in subcollections.
// you can use like docData["yourfieldnameinsubcollection"];

                  String yukID = docData[FirestoreFields.yukID]; 
                  String yukBaslik = docData[FirestoreFields.yukBaslik];
                  String icerik = docData[FirestoreFields.icerik];
                  String agirlik = docData[FirestoreFields.agirlik];
                  return Card(
                    child: Container(
                      color: ColorConstants.birincilRenk,
                      height: 210,
                      child: Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: Wrap(
                          children: [
                            Text(yukID, style: const TextStyle(fontSize: 16)),
                            const Divider(thickness: 1),
                            Text(yukBaslik,
                                style: const TextStyle(fontSize: 20)),
                            const Divider(thickness: 1),
                            Text(icerik, style: const TextStyle(fontSize: 16)),
                            const Divider(thickness: 1),
                            Text(agirlik, style: const TextStyle(fontSize: 16)),
                          ],
                        ),
                      ),
                    ),
                  );
                },
                itemCount: snapshot.data!.docs.length,
              );
            } else {
              return const Text("no data ");
            }
          } else {
            return const Text("loading");
          }
        },
      ),

【问题讨论】:

  • 我在这里用 // **** 错误标记它****。查看第二个和第三个代码块。
  • 看起来 firebase 承诺失败了。用 streambuilder 包装你的 firebase 调用。在您的异步函数 getData 中调用 firebase 端点,然后将数据返回到流构建器。尝试捕获 getData。
  • @GoldenLion 的最后一条评论对您有帮助吗?如果是,您能否请金狮将其添加为帮助社区的答案?谢谢。
  • 我自己想出来的。
  • @TolgaYılmaz 您能否将解决方案添加为帮助社区解决相同问题的答案?谢谢。

标签: firebase flutter google-cloud-firestore future flutter-getx


【解决方案1】:

我没有仔细阅读你的整个代码,因为它太多了,但从错误消息中我可以帮助你理解问题。

她是您可能想要达到的目标的简化版本:

// This method returns the data from Firestore collection
Stream<T> getData(){
  return collection.snapshots(); // This returns a Stream
  // get() returns a Future
  // snapshots() returns a Stream
}

// As the name says, it build from a STREAM
StreamBuilder(
  future: getData(), // the source of stream
  builder: (context,snapshot){
    if(snapshot.hasData){  // checking if the stream's snapshot has data
      return Text(snapshot.data!);   // If there is data, we display widgets accordingly
    }else{
      return const CircularProgressIndicator();    // If there isn't data yet, we display a CircularProgressIndicator
    }
  }
)

现在解决您的问题,如错误消息所述:

The body might complete normally, causing 'null' to be returned, but the return type is a potentially non-nullable type. Try adding either a return or a throw statement at the end.

下面是解释的例子:

StreamBuilder(
  future: getData(), // the source of stream
  builder: (context,snapshot){
    if(snapshot.hasData){  // checking if the stream's snapshot has data
      return Text(snapshot.data!);   // If there is data, we display widgets accordingly
    }
  }
)

如果你注意到了,我在这里没有使用 else 语句,所以这意味着: 如果快照有数据然后显示一个小部件,但是如果快照还没有数据,那么将显示什么。我会收到和你一样的错误。为了解决这个问题,我使用了else 语句。这样我的

我的身体不返回 null

【讨论】:

  • 感谢您的回复。但我知道如何流式传输 'collection.snapshot()' 。但这并不能满足我的需求。请你分析一下我的代码好吗?
【解决方案2】:

订阅者可以收听 Streambuilder。 MyClassBloc 包含一个名为 MyClassListStream 的 get 方法。 MyClassBloc 包含列表类型的数据流。 MyClassBlock 可以从 web api 提供者分配数据到 listMyClass 变量。在 _loadMyClassItems 方法中,调用了对 initializeStream 的调用,通知所有订阅者流上有数据。 _buildList 方法有一个流构建器,流指向 MyClassBloc 流控制器获取 MyClassListStream 评估 _MyClassListSubject.stream 和 initialData 引用 widget.blocMyClass.listMyClass。当数据到达流时,Streambuilder 映射流中的数据以创建 CardWidget 列表。 snapshot.data 是 MyClass 类型。因此 _buildList 返回从流创建的卡片小部件列表。一切从 initState() 调用 _loadMyClassItems 开始,web api 在 Promise 完成后返回 MyClass 对象列表并将数据分配给 MyClassBloc 变量并分配数据并调用 initializeTheStream。从 MyClassBloc _MyClassListSubject.add(listMyClass) 事件通知 Streambuilder 数据在流上。 Streambuilder 然后返回一个 CardWidgets 列表。

class MyClassBloc {
  List<MyClass> listMyClass;

  Stream<List<MyClass>> get MyClassListStream =>
      _MyClassListSubject.stream;
  final _MyClassListSubject =
      BehaviorSubject<List<MyClass>>();

  initializeTheStream() {
    if (listMyClass != null) {
      _MyClassListSubject.add(listMyClass);
    }
  }

  dispose() {
    _MyClassListSubject.close();
  }
}


class MyClass
 {
        int field1;
        String field2;

        MyClass(
        this.field1,
        this.field2,
        );

        Map<String,dynamic> toJson(){
   var map={
       'field1': field1,
        'field2': field2,
      };
      return map;
      }

        factory MyClass.fromJson(Map<String,dynamic> json){
        if(json==null){throw FormatException("null json");}
        return MyClass(
        json['field1'] as int,
        json['field2'] as String,
        );
  }

    }

 _loadMyClassItems(BuildContext context) {
    try {

      Provider.of<ApiWidget>(context, listen: false)
          .getMyClass()
          .then((value) {
        setState(() {
          loadSummary = true;
          if (value != null) {
            widget.blocSummary.listMyClass = value;
            widget.blocSummary.initializeTheStream();
          } else {
            widget.blocSummary.listMyClass =
               [];
            widget.blocSummary.initializeTheStream();
          }
        });
      }).catchError((error) {
        DialogCaller.showErrorDialog(context, error);
      });
    } catch (e) {
      DialogCaller.showErrorDialog(context, e.toString());
    }
  }




Widget _buildList(BuildContext context) {
    List<Widget> cardWidgets;
    return StreamBuilder<List<MyClass>>(
        stream: widget.blocMyClass.MyClassListStream,
        initialData: widget.blocMyClass.listMyClass,
        builder: (context, snapshot) {
          //debugPrint(snapshot.connectionState.toString());
          if (!snapshot.hasData) {
            return PleaseWaitWidget();
          } else if (snapshot.hasError) {
            DialogCaller.showErrorDialog(
                    context, "future builder in main has an error")
                .then((value) {});
          } else if (snapshot.hasData) {
            //debugPrint("reached processing");
            cardWidgets =
                snapshot.data.map((MyClass MyClass) {
              return CardWidget(MyClass);
            }).toList();
            return CardWidgets.length == 0
                ? Container(
                    padding: EdgeInsets.all(20),
                    child: Text("No Records found", style: NoRecordFoundStyle))
                : ListView(children: CardWidgets);
          }
          return (Container(child: Text("Failed to build list")));
        });
  }


myWidget.dart

void initState(){
  super.initState();
 _loadMyClassItems();
}
Widget build(BuildContext context) {
    if (loading == false) {
      return PleaseWaitWidget();
    } else {
      return new Scaffold(
          key: _scaffoldKey,
     
            ....

     body: Column(children: [
            Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Expanded(child: _buildList(context)),
     ]

【讨论】:

    猜你喜欢
    • 2020-09-02
    • 2013-01-23
    • 2021-10-07
    • 1970-01-01
    • 2016-06-25
    • 2021-05-22
    • 2020-02-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多