Skip to content

兄弟们,今天写 Flutter 又踩坑了

今天本来想安安静静写个功能,就是把一些数据写到手机本地文件里。结果 App 直接给我闪退,后台报了这么一长串红字:

E/flutter (19945): 
E/flutter (19945): [ERROR:flutter/runtime/dart_vm_initializer.cc(40)] Unhandled Exception: Bad state: StreamSink is bound to a stream
E/flutter (19945): #0      _StreamSinkImpl.close (dart:io/io_sink.dart:204:7)
E/flutter (19945): #1      _LocalFilePageState.reader.<anonymous closure> (package:firest/page/more/local_page/local_page.dart:90:14)
... (省略一堆看不懂的)

Bad state: StreamSink is bound to a stream,这玩意儿直接给我整蒙了。“流处理器已经绑定到一个流”?这是啥意思?我寻思我也没对它做啥奇怪的操作啊。


问题出在哪?

查了半天资料,又结合官方的说法,大概搞明白了。

这个错误的意思是,你试图关闭一个 StreamSink(可以理解为一个“数据写入的口子”),但它当时正处于一个“工作状态”或者“状态不对”,不允许你关。

通常是我们手动操作 IOSinkStreamSink 的一种)时,代码写得太复杂,或者在某个异步操作之后,它的状态已经不是我们想的那样了,这时候强行 close() 就会触发这个异常。

我之前的代码大概是这样的,想手动控制流的写入和关闭:

dart
// 导致问题的代码(示意)
Future<void> problematicWrite(File file, Stream<List<int>> dataStream) async {
  var sink = file.openWrite(); // 1. 打开一个写入流
  
  // 2. 把一个数据流(dataStream)的内容写进去
  await sink.addStream(dataStream); 
  
  // ... 可能还有其他一些异步操作 ...

  // 3. 在这里关闭,但此时 sink 的状态可能已经因为它内部的工作流而变得复杂
  await sink.close(); // <--- 罪魁祸首就在这!
}

正确的姿势是啥?

后来发现,其实是我想复杂了。Flutter 对于文件读写这种常用操作,早就提供了更简单、更安全的“一条龙”方法。

核心就是:别自己手动管 IOSink 的打开和关闭了!

直接用 File 对象自带的 writeAsBytes() 方法,它会帮你搞定所有事:打开文件 -> 写入数据 -> 关闭文件。全程自动化,根本不给你犯错的机会。

修改后的代码就清爽多了:

dart
import 'dart:io';
import 'dart:typed_data';

// 正确的姿势
Future<void> saveFile(String filePath, Uint8List data) async {
  try {
    var file = File(filePath);

    // 就这一行代码,搞定!
    // 它内部会处理好打开、写入、关闭的所有流程
    await file.writeAsBytes(data);

    print('文件写入成功!');
  } catch (e) {
    print('写入文件时出错了: $e');
  }
}

总结一下

所以,下次再遇到 Bad state: StreamSink is bound to a stream 这个错,别慌。

99% 的可能性是你手动操作 IOSinkStreamSink 时,在某个环节把它状态搞乱了。

最省事的解决办法就是:

放弃手动管理流,直接用 file.writeAsBytes() (写字节) 或者 file.writeAsString() (写字符串)。

让官方封装好的方法帮你处理这些复杂的流操作,不仅代码更简洁,还更安全。

行了,今天的水文就到这,希望能帮到同样踩坑的兄弟。