【Flutter】使用Stream+StreamBuilder实现ChatGPT的回答效果
Demo地址:StreamBuilder_test
参考了网上各种资料和代码,做个简单的小总结吧。
Talk is cheap. Show me the code.
1. 请求数据
使用http.Client
发起请求,返回的响应类型是StreamedResponse
,可以不用等全部数据都拼接好才拿到最终结果,可以实时获取服务器传过来的数据,给多少就展示多少,跟水流一样。
/// 发起请求
void askGPT(String problem) {
final messages = [
{
"role": "user",
"content": problem,
},
];
final requestBody = {
"model": apiModel,
"messages": messages,
"temperature": 0.5,
"stream": true // 想流式接收数据必须填写该参数!
};
var request = http.Request("POST", Uri.parse(apiURL));
request.headers["Authorization"] = "Bearer $apiKey";
request.headers["Content-Type"] = "application/json; charset=UTF-8";
request.body = jsonEncode(requestBody);
// 开始请求
http.Client().send(request).then((response) {
String showContent = "";
final stream = response.stream.transform(utf8.decoder);
// 监听接收的数据
stream.listen((data) {
final dataLines = data.split("\n").where((element) => element.isNotEmpty).toList();
for (String line in dataLines) {
// 丢弃前6个字符:“data: ”
if (!line.startsWith("data: ")) continue;
final data = line.substring(6);
if (data == "[DONE]") break; // 表示接收已完成
// 解析数据
Map<String, dynamic> responseData = json.decode(data);
List<dynamic> choices = responseData["choices"];
Map<String, dynamic> choice = choices[0];
Map<String, dynamic> delta = choice["delta"];
String content = delta["content"] ?? "";
// 拼接并展示数据
showContent += content;
_streamController.add(showContent);
if (choice["finish_reason"] != null) break; // 表示接收已完成
}
},
onDone: () => _streamController.add(showContent),
onError: (error) => _streamController.addError(error),
);
});
}
2. 展示数据
使用StreamController
异步接收数据,搭配StreamBuilder
可以不断地更新组件,不需要使用setState
,实现局部刷新。
StreamController<String> _streamController = StreamController<String>();
@override
Widget build(BuildContext context) {
return Center(
child: StreamBuilder(
stream: _streamController.stream,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasError) return Text("发生错误: ${snapshot.error}");
if (snapshot.hasData) {
// 有新数据就刷新,这里会执行多次
String content = snapshot.data ?? "";
if (content.length > 0) {
return Text(content);
}
}
// 正在请求,转圈等待
return CircularProgressIndicator();
},
),
);
}
效果展示
同样,剩下的无非就是UI、Markdown解析、数据存储和请求上下文的拼接,弄好就是一个ChatGPT的App了。这只是Stream
的基本使用,还有许多高级功能和复杂的用法,需要继续学习并深入了解。
转载自:https://juejin.cn/post/7236372620999147581