When working with FastAPI‘s StreamingResponse (especially for OpenAI-like APIs), Flutter Developers need to handle data as a stream – Dio + Stream is the recommended approach.
Why Streaming ?
Since the server returns data chunk by chunk (streamed), We can’t treat the response as a normal JSON body. Instead, We bind and listen to the stream line-by-line.
If the chunks are full JSON strings, You can use a Completer to wait entire response. If not, just buffer the parts you need.
Code Implementation
try {
{
final Map<String, dynamic> paramsChatCompletions = {
"param1": "value1",
"param2": value2,
};
final dio = Dio();
final response =
await dio.post<ResponseBody>('$yourBaseURL/api/chat/completions',
options: Options(
responseType: ResponseType.stream,
headers: {
"Authorization": "Bearer $yourToken",
"Accept": "text/event-stream",
},
),
data: paramsChatCompletions);
Stream<String> stream = utf8.decoder.bind(response.data!.stream);
final completer = Completer<void>();
String buffer = "";
String fullDataResponse = "";
String id = "";
stream.listen(
(chunk) {
for (var line in LineSplitter.split(chunk)) {
if (line.startsWith('data')) {
final jsonString = line.substring(5).trim();
if (jsonString == '[DONE]') return;
if (jsonString.startsWith('{') && jsonString.endsWith('}')) {
try {
final jsonData = jsonDecode(jsonString);
id = jsonData['id'];
final content = jsonData['choices'][0]['delta']['content'];
if (content != null && content.toString().isNotEmpty) {
buffer += content;
}
} catch (e) {
log('json decode error: ${e.toString()}');
}
} else {
fullDataResponse += line;
}
} else {
fullDataResponse += line;
}
return;
}
},
onDone: () {
if (buffer.isEmpty) {
if (fullDataResponse.endsWith('}')) {
try {
final jsonString = fullDataResponse.substring(5).trim();
final jsonData = jsonDecode(jsonString);
id = jsonData['id'];
final content = jsonData['choices'][0]['delta']['content'];
if (content != null && content.toString().isNotEmpty) {
buffer += content;
}
} catch (e) {
print("Decode error: $e");
}
}
}
// add Message to List
// emit update data state
completer.complete();
},
onError: (e) {
print("Stream error: $e");
// emit update data state with error
completer.complete();
},
);
await completer.future;
}
} catch (e) {
// emit update data state with error
}
No comment