我正在尝试将Websocket与该软件包websocket一起使用,一切都很好,直到我意识到如果通道第一次无法与服务器连接,流将为空,并且出现此错误:>
getter'stream'在null上被调用
我将频道设置为Singleton类,因此我可以在应用程序中调用close或在我想要添加的任何位置添加内容:
class WebSocket {
static IOWebSocketChannel channel;
static init() async {
try {
String macAddress = await getMacAddress();
channel = IOWebSocketChannel.connect("ws://172.16.0.39:8001/ws/fsmart-door/$macAddress");
} catch (e) {
debugPrint(e.toString());
}
}
}
我在主目录中调用Websocket.init()以便通道可以进行流传输,但是如果IOWebSocketChannel.connect无法连接,则应用程序也将卡住。
这是我的StreamBuilder:
StreamBuilder(
stream: WebSocket.channel.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
}
return Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: Text(snapshot.hasData ? '${snapshot.data}' : ''),
);
},
答案 0 :(得分:1)
问题是当连接失败时,channel
为null
,因此您无法进行channel.stream
并且StreamBuilder
不能拥有Stream
。因此,您可以采取的一种方法是在Stream
中放置一个WebSocket
来处理重试,并在channel
准备就绪时,将来自channel.stream
的数据传递给WebSocket
Stream
。
WebSocket
可能是这样:
class WebSocket {
static WebSocket _instance;
final _streamController = StreamController.broadcast();
IOWebSocketChannel channel;
static WebSocket get instance {
if (_instance == null) {
_instance = WebSocket();
}
return _instance;
}
Stream get channelStream => _streamController.stream;
Future init(int retries) async {
if (channel == null) {
await _tryToConnect(retries);
if (channel != null) {
_streamController.sink.add(null);
channel.stream.listen((value) {
_streamController.sink.add(value);
});
} else {
_streamController.sink.addError("Could not connect");
}
}
}
Future _tryToConnect(int retries) async {
if (channel == null) {
try {
String macAddress = await getMacAddress();
// This is to throw exception if it can't connect (https://github.com/dart-lang/web_socket_channel/issues/38#issuecomment-450383558)
final socket = await WebSocket.connect(
"ws://172.16.0.39:8001/ws/fsmart-door/$macAddress")
.timeout(_webSocketConnectionTimeout);
channel = IOWebSocketChannel(socket);
} catch (e) {
debugPrint(e.toString());
}
if (channel == null && retries > 0) {
_streamController.sink.addError("Retries left: ${retries - 1}");
await _tryToConnect(retries - 1);
}
}
}
void close() => _streamController.close();
}
这将是StreamBuilder
:
StreamBuilder(
stream: WebSocket.instance.channelStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('Data: ${snapshot.data}');
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
return Text('Loading...');
},
)
如果要对此解决方案进行快速测试,请为此替换try
catch
中的两行:
await Future.delayed(Duration(seconds: 3));
if (retries == 2) channel = IOWebSocketChannel();
使用此IOWebSocketChannel
class IOWebSocketChannel {
var _streamController = StreamController<String>.broadcast();
IOWebSocketChannel() {
init();
}
Stream get stream => _streamController.stream;
Future init() async {
await Future.delayed(Duration(seconds: 3));
_streamController.sink.add("1");
await Future.delayed(Duration(seconds: 3));
_streamController.sink.add("2");
}
void close() => _streamController.close();
}
并在StatefulWidget
中使用此示例:
@override
void initState() {
super.initState();
WebSocket.instance.init(5);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StreamBuilder(
stream: WebSocket.instance.channelStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('Data: ${snapshot.data}');
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
return Text('Loading...');
},
),
),
);
}