Flutter

Cloud Functions Streaming in Flutter

Introducing Cloud Functions Streaming in Flutter: A Practical Guide

3 min readCloud Functions Streaming in Flutter

Cloud Functions have been a key part of building seamless serverless experience with Firebase. Now, with the introduction of streaming support, you can receive real-time updates from your functions β€” enabling more responsive and interactive user experiences.

In this tutorial, I'll walk you through how to stream data from a Cloud Function in Flutter using the new stream() API in the cloud_functions package.

To keep things simple and fun, we’ll use a Cloud Function that generates random fruits over time and streams them to the Flutter app one by one.

Setting up the Cloud Function

First, let's add streaming capabilities to a callable function on the backend. Below is the Node.js implementation:

// Simulate a fruit generator
function generateRandomFruit() {
  const fruits = ["🍎 Apple", "🍌 Banana", "πŸ‡ Grape", "🍊 Orange", "πŸ‰ Watermelon", "πŸ’ Cherry"];
  return fruits[Math.floor(Math.random() * fruits.length)];
}

exports.generateFruits = onCall(async (request, response) => {
  const count = request.data.count ?? 5; 
  const fruitList = [];

  for (let i = 0; i < count; i++) {
    // Simulate delay (e.g., long-running task)
    await new Promise(resolve => setTimeout(resolve, 3000));

    const fruit = generateRandomFruit();

    if (request.acceptsStreaming) {
      response.sendChunk({ index: i, fruit });
    }

    fruitList.push(fruit);
  }

  // Return full list for non-streaming clients
  return fruitList;
});

This function simulates a slow data generation process by introducing a short delay between each fruit. If the client supports streaming, each fruit is sent as it's generated. Otherwise, the full list is returned at the end.

For a deeper dive into how streaming works with Cloud Functions in Node.js, check out this blog post.

Consuming the Stream in Flutter

Now that our backend is ready to stream data, let's move on to the Flutter side. We'll use the cloud_functions package to connect to our function and handle streaming with the new stream() API.

Make sure you have the necessary dependencies in your pubspec.yaml:

dependencies:
  firebase_core: ^3.13.1
  cloud_functions: ^5.4.1

Now, here's how you can stream fruits from the generateFruits function:

import 'package:cloud_functions/cloud_functions.dart';

void streamFruits() async {
    final callable =       FirebaseFunctions.instance.httpsCallable('generateFruits');

callable.stream({'count': 4}).listen(
    (StreamResponse data) {
      switch (data) {
        case Chunk(:final partialData):
	   print('New fruit: ${partialData}');
          break;
        case Result(:final result):
          print('All fruits: ${result.data}');
          break;
      }
    },
    onError: (error) {
      print('Stream error: $error');
    },
    onDone: () {
      print('Stream complete.');
    },
  );
}

In the example above:

  • We pass { 'count': 4 } to the function to specify the number of fruits to receive.
  • The stream emits values of type StreamResponse, which can be either:
    • Chunk: Represents a partial piece of data (a single fruit).
    • Result: The final, complete response (the full fruit list), marking the end of the stream.

When you run the function, your console output might look like this:

New fruit: {index: 0, fruit: πŸ‡ Grape}
New fruit: {index: 1, fruit: πŸ’ Cherry}
New fruit: {index: 2, fruit: 🍊 Orange}
New fruit: {index: 3, fruit: πŸ‡ Grape}
All fruits: [πŸ‡ Grape, πŸ’ Cherry, 🍊 Orange, πŸ‡ Grape]

We've also introduced support for Web API-style AbortSignal via the webAbortSignal option in HttpsCallableOptions. This allows you to cancel the stream from the client side in web apps.

You can use one of three signal types:

  • TimeLimit: Automatically aborts after a specified duration.
  • Abort: Immediately aborts, optionally with a reason.
  • Any: Aborts when any of the provided signals abort.

Here’s a usage example with a 4-second timeout:

final options = HttpsCallableOptions(
  webAbortSignal: TimeLimit(const Duration(seconds: 4)),
);

Conclusion

That wraps up this tutorial on streaming data from Cloud Functions in Flutter. With just a few lines of code, you can start sending real-time updates from your backend to your Flutter app, improving responsiveness and enhancing user engagement.

Thanks for following along, and happy coding!