Home > database >  StreamBuilder: A non-null value must be returned since the return type 'Stream' doesn'
StreamBuilder: A non-null value must be returned since the return type 'Stream' doesn'

Time:11-27

I'm using a StreamBuilder widget to show the score in real-time (fetching the data from API and then passing it to the stream) but while doing this process, it is throwing up the following error

"A non-null value must be returned since the return type 'Stream' doesn't allow null"

Here is the Code

class Match extends StatefulWidget {

  @override
  State<Match> createState() => _MatchState();
}

class _MatchState extends State<Match> {
  
    getCricketScoreApi() async {
    var url = Uri.parse(api_url);
    var response = await get(url);
    if(response.statusCode == 200){
      final score = jsonDecode(response.body);
      return  score['cricket_score'];
    } else {
      return print('Something went wrong');
    }
  }

  Stream streamScore() { 
    return Stream.periodic(Duration(seconds: 1), (Timer) => getCricketScoreApi()); // Calling method after every 1 second
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[200],
      body: StreamBuilder(
          stream: streamScore(),
          builder: (context, snapshot){
              var cricket_score_api = snapshot.data.toString();
              if(snapshot.hasData){
                return Center(
                  child: Text(cricket_score_api, style: TextStyle(color: Colors.grey, fontSize: 25.0)), // Error: Instance of Future
                );
              } else {
                return Text('Null');
              }
          }
      ),
    );
  }
}

CodePudding user response:

So as you say, the original error came from not returning a value.

The current error is even simpler, you ARE returning a value, but that value is, an instance of Future, so when you call snapshot.data.toString();, you get the literal text instance of Future

I came up with two ways of fixing the issue, here they are now:

1. Make your stream function an async* function:

Stream streamScore() async* {
  while (true) {
    await Future.delayed(Duration(seconds: 1));
    yield await getCricketScoreApi();
  }
}

async* functions work like async functions, but instead of returning a future, they return a stream immediately and add values to the stream progressively using the yield keyword.

The one thing I don't love about the approach above is the usage of while (true), it seems unsafe.

2. Deal with the future

Stream<Future> streamScore() {
  ...
}
...
body: StreamBuilder<Future>(
    stream: streamScore(),
    builder: (context, snapshot){
        if(snapshot.hasData) {
          return FutureBuilder(
            future: snapshot.data!
            builder: (context, snapshot1) {
              if (snapshot.hasData) {
                return Center(child: Text(snapshot1.data!.toString()));
              } 
              return CircularProgressIndicator();
            }
          );
        } else {
          return Text('Null');
        }
    }
),

Hopefully one of the above solutions fix the new issue?

  • Related