keepcalculator

06 Numbers and Powers

Flutter and React Native AI Token Streaming Guide

Card No. 06·36400 · Updated · Standard formula, shown below

To stream AI responses token by token in Flutter or React Native, send a request to your own backend and relay the model’s streamed output to the app using Server-Sent Events (SSE) or a streaming HTTP response. Keep the provider API key on the server, handle partial text and connection errors in the client, and treat each token as untrusted content until it is safely displayed.

How token streaming works

“Token by token” usually means that the model’s output is delivered in small chunks as it is generated, rather than waiting for the complete answer. A chunk may contain a whole word, part of a word, punctuation, or several tokens; do not assume that each network event corresponds to one model token.

A typical flow has three parts:

  1. The app sends the user’s prompt and conversation context to an authenticated endpoint on your backend.
  2. The backend validates the request, applies authorization and usage limits, then calls the AI provider with streaming enabled.
  3. The backend forwards output events to the app, which appends text to the current assistant message and updates the interface.

The backend is important even if the mobile app can technically call a provider directly. An API key embedded in an app can be extracted from the installed package or observed at runtime. Obfuscation does not make a long-lived secret safe. A server-side proxy keeps provider credentials out of the app and gives you a place to enforce access rules, limits, logging policies, and cancellations.

Choose a transport and event format

SSE is a practical choice for one-way delivery from server to client. It uses an HTTP response whose content type is text/event-stream; events are separated by blank lines, and a data line commonly contains JSON. The app sends the prompt in a normal POST request, and the server streams the response back. This avoids putting a large prompt into a URL.

Use a streaming HTTP response instead if your client libraries and infrastructure already handle incremental response bodies reliably. WebSockets suit conversations that need ongoing two-way messages over one connection, but they add connection and reconnection behavior that a simple request-and-stream design may not need. Choose based on your server, proxy, and mobile networking support—not on the assumption that one transport guarantees lower latency everywhere.

Define an application-level event contract instead of exposing provider-specific events directly. For example:

  • start: a response has begun, optionally with a request identifier.
  • delta: JSON containing a text fragment to append.
  • done: the response completed normally.
  • error: a safe, user-readable failure category.

Provider streams may include metadata, tool calls, usage information, or special end markers. Translate these on the backend; do not blindly show raw protocol messages as assistant text.

Secure backend implementation

Build an endpoint that authenticates the app’s user and accepts only the input your application needs. Validate prompt size and conversation structure, check authorization, and apply per-user limits. Keep provider credentials in server-side secret configuration, never in source code shipped with the app, a downloadable configuration file, or a mobile build setting.

When streaming, flush each complete event promptly and set suitable response headers. For SSE, that generally includes Content-Type: text/event-stream and disabling intermediary buffering where the hosting platform supports that setting. Configure proxy and server timeouts for long responses. Send periodic comments or heartbeats only if needed to keep idle connections alive through your infrastructure; clients should not display them as content.

Handle upstream failures before and after streaming begins. Before the first event, return an appropriate HTTP error status. Once headers and stream data have been sent, the server may no longer be able to change the HTTP status, so send a defined error event and close cleanly. Do not forward stack traces, provider credentials, internal URLs, or sensitive diagnostics to the app. Avoid logging full prompts and generated text unless there is a justified, disclosed need and suitable data handling controls.

Propagate cancellation: if a user stops generation or leaves the screen, cancel the client request and have the backend abort its upstream provider request where supported. This reduces unnecessary work and avoids continuing to generate a response nobody is using.

Flutter client steps

  1. Choose a maintained networking approach that exposes incremental response data or supports SSE. Confirm that it works on the platforms you ship; ordinary code that waits for a complete response body will not provide token streaming.
  2. POST the prompt to your backend over HTTPS, attaching the app’s normal user authentication. Do not attach the AI provider’s secret key.
  3. Parse the stream according to its framing. With SSE, accumulate bytes and decode text carefully: a network chunk can split a line or a multibyte character. Then parse complete events and their data fields.
  4. On each delta, update the active assistant message. Avoid rebuilding an entire long conversation expensively for every tiny fragment; batch UI updates briefly if needed while preserving the streamed appearance.
  5. Handle done, error, timeout, and cancellation distinctly. Close subscriptions and release resources when the request ends or the screen is disposed.

Flutter’s widget lifecycle matters: asynchronous callbacks should not update disposed state. Use the state-management pattern already used by the app, and ensure a retry creates a new response rather than accidentally appending a second answer to the previous partial one.

React Native client steps

  1. Use a networking library or platform-supported approach that exposes incremental response data or SSE events. The widely used Fetch interface is not a guarantee of identical streaming behavior across every React Native runtime and platform, so verify the exact runtime and networking stack you target.
  2. Send an authenticated POST to your backend. If the chosen SSE client only supports GET, do not put prompts or private conversation data in query parameters; use a POST-capable streaming client or a short-lived, narrowly scoped server-issued session mechanism.
  3. Decode and parse events incrementally, preserving incomplete data between chunks. Append only the event’s text delta, not the JSON envelope or event name.
  4. Keep stream state separate from the submitted prompt and earlier messages. Use a functional state update or a suitable store so rapid deltas do not overwrite one another because of stale state.
  5. Abort the request when the user taps Stop or the screen no longer needs it, and remove listeners or subscriptions. Test behavior on both iOS and Android, including backgrounding and network changes.

Testing, security, and common failures

Test the complete path through the production-like proxy or gateway: a server can flush correctly while an intermediary buffers the response until it is complete. Measure time to first visible text as well as total completion time. Also test slow connections, interrupted Wi-Fi, expired authentication, server timeouts, empty output, and cancellation.

  • Everything arrives at once: check that the client reads incrementally and that proxies, compression, or hosting layers are not buffering the response.
  • Broken or missing text: check byte decoding, event boundaries, JSON parsing, and whether a provider-specific end event is being mistaken for content.
  • Duplicate output: ensure retries create a new message and that reconnect logic does not replay already-rendered deltas without deduplication.
  • Leaked credentials: inspect app bundles and network requests; only the backend should communicate with the provider using its secret credential.

Generated output is untrusted. If you render Markdown, HTML, links, or code, use a renderer that safely handles untrusted input; do not inject streamed text as raw HTML. Consider displaying plain text while streaming and rendering supported formatting after completion. Streaming also means partial output may be visible before the model finishes, so the interface should distinguish an in-progress response from a completed one.

Frequently Asked Questions

Can a Flutter or React Native app call an AI provider directly?
It may be technically possible, but embedding a reusable provider secret in a distributed app is not secure. Use a backend that authenticates users and keeps provider credentials server-side.

Is SSE better than WebSockets for AI responses?
SSE is often simpler when the server streams output in one direction and the app sends requests separately. WebSockets can fit sustained two-way communication. Neither choice removes the need for authentication, cancellation, and robust reconnection handling.

Does one streamed event equal one token?
No. Events and network chunks are transport units. They may contain part of a word, multiple tokens, or metadata, so append the text supplied by your event contract rather than counting chunks as tokens.

Why does streaming work locally but not in production?
A reverse proxy, CDN, compression layer, serverless platform, or client networking implementation may buffer data or impose timeouts. Test through the same infrastructure used in production and configure streaming and timeout behavior there.

Should the app reconnect automatically after a dropped stream?
Only if the protocol supports safe resumption. Without event identifiers and server-side tracking, reconnecting can lose or duplicate output. A simpler first version can show the partial answer and offer a fresh retry.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →