Search code examples
flutterstream-builderdart-null-safety

Correcting Null Safety issue with StreamBuilder in Flutter app


I have an null safety issue with my StreamBuilder in my flutter app.

On the open bracket "{" of the builder: property I am getting this error The body might complete normally, causing 'null' to be returned, but the return type is a potentially non-nullable type.

Here is the code for the StreamBuilder.

StreamBuilder (
        stream: _db.collection('agency').doc(globals.agencyId).
        collection('trxns').doc(globals.currentTrxnId).snapshots(),
        builder: (BuildContext context, AsyncSnapshot trxnSnapshot) {
          if (trxnSnapshot.hasData) {
            var outPut = (trxnSnapshot.data() as QueryDocumentSnapshot);
            clientFNameController.text = trxnSnapshot.data.data['clientFName'] ?? "";
          }
),

I tried to add a type like this: StreamBuilder ( but I get this error: The argument type 'Stream<DocumentSnapshot<Map<String, dynamic>>>' can't be assigned to the parameter type 'Stream<QuerySnapshot<Object?>>?'.

Now I change the type to match the statement above and now I am back at the original error message. Here is what I changed the type to.

StreamBuilder <DocumentSnapshot<Map<String, dynamic>>>(
        stream: _db.collection('agency').doc(globals.agencyId).
        collection('trxns').doc(globals.currentTrxnId).snapshots(),
        builder: (BuildContext context, AsyncSnapshot trxnSnapshot) {
          if (trxnSnapshot.hasData) {
            var outPut = (trxnSnapshot.data() as QueryDocumentSnapshot);
            clientFNameController.text = trxnSnapshot.data.data['clientFName'] ?? "";
          }
),

I don't know what is wrong or how to fix it. I know I need either this "!" or this "?" but I don't know which one or where to put it.

I would really appreciate some help here.


Solution

  • A StreamBuilder must return a Widget in its builder parameter. If you don't need to show any Widget (just do some background update), you can use a StreamSubscription instead:

    class _MyWidgetState extends State<MyWidget> {
      late final StreamSubscription<DocumentSnapshot> _subscription;
     
      @override
      void initState() {
        super.initState();
      
        final Stream<DocumentSnapshot> stream = _db
            .collection('agency')
            .doc(globals.agencyId)
            .collection('trxns')
            .doc(globals.currentTrxnId)
            .snapshots();
    
        _subscription = stream.listen((data) {
          if (data == null) return;
          setState(() => clientFNameController.text = data['clientFName'] ?? "");
        });
      }
    
      @override
      void dispose() {
        _subscription.cancel();
        super.dispose();
      }
    }
    

    However, if you want to keep using StreamBuilder, you can

    1. just return an empty Widget (not really a good practice):
    StreamBuilder(
      stream: _db
          .collection('agency')
          .doc(globals.agencyId)
          .collection('trxns')
          .doc(globals.currentTrxnId)
          .snapshots(),
      builder: (BuildContext context, AsyncSnapshot trxnSnapshot) {
        if (trxnSnapshot.hasData) {
          var outPut = (trxnSnapshot.data as QueryDocumentSnapshot);
          clientFNameController.text = outPut.data['clientFName'] ?? "";
        }
        return SizedBox.shrink();
      },
    ),
    
    1. return a meaningful Widget based on each operation:
    StreamBuilder(
      stream: _db
          .collection('agency')
          .doc(globals.agencyId)
          .collection('trxns')
          .doc(globals.currentTrxnId)
          .snapshots(),
      builder: (BuildContext context, AsyncSnapshot trxnSnapshot) {
        if (trxnSnapshot.hasData) {
          var outPut = (trxnSnapshot.data as QueryDocumentSnapshot);
          clientFNameController.text = outPut.data['clientFName'] ?? "";
          return Text("client name updated");
        }
        return Text("client name not updated");
      },
    ),