Flutter 实现流式消息接收

尽意
2026-01-07 / 0 评论 / 6 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2026年01月07日,已超过10天没有更新,若内容或图片失效,请留言反馈。

我们通过 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 对话、实时日志等场景。

1

评论 (0)

取消