我们通过 Dio 发起一个 ResponseType.stream 请求,消费后端逐字符返回的 HTTP 流。核心代码如下:
void sendMessage() => Dio().get(
"http://192.168.149.178:8080/stream/covert?url=${state.text}",
options: Options(responseType: ResponseType.stream),
cancelToken: CancelToken(),
).then((response) {
final data = response.data;
if (data is! ResponseBody) return;
state.data = "";
update();
data.stream.listen((chunk) {
final res = utf8.decoder.convert(chunk);
state.data += res;
update();
});
});UI 部分使用 Expanded + SingleChildScrollView 展示实时追加的文本:
Column(
children: [
TextField(controller: logic.text),
TextButton(onPressed: logic.sendMessage, child: Text("发送消息")),
Expanded(
child: SelectionArea(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [Text(state.data)],
),
),
),
),
],
)后端使用 Spring WebFlux 将目标网页内容逐字符延迟返回:
@GetMapping("/covert")
public Flux<String> covert(@RequestParam String url) {
return Mono.fromCallable(() -> {
// 同步抓取网页内容(在非 Netty 线程执行)
var request = new Request.Builder().url(url).build();
try (var resp = client.newCall(request).execute()) {
return resp.body().string();
}
})
.flux()
.flatMap(text ->
Flux.fromStream(text.chars().mapToObj(c -> String.valueOf((char) c)))
.delayElements(Duration.ofMillis(10)) // 每字符间隔 10ms
);
}该方案无需 SSE 或 WebSocket,仅依赖标准 HTTP 分块传输,即可实现“打字机式”流式响应,适用于 AI 对话、实时日志等场景。
评论 (0)