Dart中如何在WebSocket数据更新后立即获取数据?
问题描述
我通过WebSocket事件获取数据,但需要在数据到达WebSocket时立即更新页面数据。我尝试过用Stream,但目前只能通过Stream.periodic(Duration(seconds: 2))实现,而且返回的是静态Future——Future没办法订阅更新,只能刷新页面才能拿到新数据。
但用Stream.periodic时,页面里的WebSocket图片会闪烁,我需要图片保持静态,同时数据能即时更新。
请问该怎么修改?能不能结合ValueListenableBuilder和WebSocket来实现?
现有代码
List wanted = []; static Stream<List?> getWanted() => Stream.periodic(Duration(seconds: 10)) .asyncMap((event) => fetchWantedFixation()); static Future<List> fetchWantedFixation() async { final httpConnectionOptions = HttpConnectionOptions( accessTokenFactory: () => SharedPreferenceService().loginWithToken(), skipNegotiation: true, transport: HttpTransportType.WebSockets); final hubConnection = HubConnectionBuilder() .withUrl( 'http://my securelink/', options: httpConnectionOptions, ) .build(); await hubConnection.start(); String? wantedFixation; Future.delayed(const Duration(seconds: 7)) .then((value) => alarmplayer.StopAlarm()); hubConnection.on('Wanted', (arguments) { HapticFeedback.mediumImpact(); // SystemSound.play(SystemSoundType.click); wanted = arguments as List; alarmplayer.Alarm(url: 'assets/wanted.mp3', volume: 0.25); }); hubConnection.onclose(({error}) { throw Exception(error); }); print(wanted); return wanted; }
解决方案
你的核心问题是重复创建WebSocket连接(Stream.periodic每10秒就会调用fetchWantedFixation,每次都新建HubConnection),这不仅浪费资源,还会引发UI闪烁。正确的做法是保持单个WebSocket连接,通过事件驱动实时推送数据到UI。
方案1:自定义Stream实现实时推送
不需要Stream.periodic,直接在WebSocket回调里将数据推送到Stream:
class WantedService { static final WantedService _instance = WantedService._internal(); factory WantedService() => _instance; WantedService._internal(); HubConnection? _hubConnection; final _controller = StreamController<List>.broadcast(); List _wanted = []; Stream<List> get wantedStream => _controller.stream; Future<void> initConnection() async { if (_hubConnection?.state == HubConnectionState.Connected) return; final httpConnectionOptions = HttpConnectionOptions( accessTokenFactory: () => SharedPreferenceService().loginWithToken(), skipNegotiation: true, transport: HttpTransportType.WebSockets, ); _hubConnection = HubConnectionBuilder() .withUrl( 'http://my securelink/', options: httpConnectionOptions, ) .build(); _hubConnection!.on('Wanted', (arguments) { HapticFeedback.mediumImpact(); _wanted = arguments as List; _controller.add(_wanted); // 实时推送新数据到Stream alarmplayer.Alarm(url: 'assets/wanted.mp3', volume: 0.25); Future.delayed(const Duration(seconds: 7)).then((_) => alarmplayer.StopAlarm()); }); _hubConnection!.onclose(({error}) { if (error != null) { print('连接关闭:$error'); // 可在此添加重连逻辑 } }); await _hubConnection!.start(); } void dispose() { _controller.close(); _hubConnection?.stop(); } }
使用方式
在页面中用StreamBuilder监听数据:
@override void initState() { super.initState(); WantedService().initConnection(); } @override Widget build(BuildContext context) { return StreamBuilder<List>( stream: WantedService().wantedStream, initialData: [], builder: (context, snapshot) { // 用snapshot.data渲染UI,数据更新时自动刷新,无闪烁 return YourImageWidget(data: snapshot.data); }, ); } @override void dispose() { WantedService().dispose(); super.dispose(); }
方案2:结合ValueListenableBuilder实现
如果更倾向用ValueNotifier,可采用以下写法:
class WantedService { static final WantedService _instance = WantedService._internal(); factory WantedService() => _instance; WantedService._internal(); HubConnection? _hubConnection; final ValueNotifier<List> wantedNotifier = ValueNotifier([]); Future<void> initConnection() async { if (_hubConnection?.state == HubConnectionState.Connected) return; final httpConnectionOptions = HttpConnectionOptions( accessTokenFactory: () => SharedPreferenceService().loginWithToken(), skipNegotiation: true, transport: HttpTransportType.WebSockets, ); _hubConnection = HubConnectionBuilder() .withUrl( 'http://my securelink/', options: httpConnectionOptions, ) .build(); _hubConnection!.on('Wanted', (arguments) { HapticFeedback.mediumImpact(); final newData = arguments as List; wantedNotifier.value = newData; // 更新ValueNotifier触发UI刷新 alarmplayer.Alarm(url: 'assets/wanted.mp3', volume: 0.25); Future.delayed(const Duration(seconds: 7)).then((_) => alarmplayer.StopAlarm()); }); _hubConnection!.onclose(({error}) { if (error != null) { print('连接关闭:$error'); // 可在此添加重连逻辑 } }); await _hubConnection!.start(); } void dispose() { wantedNotifier.dispose(); _hubConnection?.stop(); } }
使用方式
用ValueListenableBuilder监听数据:
@override void initState() { super.initState(); WantedService().initConnection(); } @override Widget build(BuildContext context) { return ValueListenableBuilder<List>( valueListenable: WantedService().wantedNotifier, builder: (context, value, child) { return YourImageWidget(data: value); }, ); } @override void dispose() { WantedService().dispose(); super.dispose(); }
关键修改点
- 用单例模式维护单个WebSocket连接,避免重复创建(这是UI闪烁的核心原因)
- 改用事件驱动:在WebSocket的
Wanted回调里直接推送数据到Stream/ValueNotifier,实现即时更新 - 移除
Stream.periodic轮询逻辑,避免无效请求 - 添加
dispose方法,释放资源防止内存泄漏
内容的提问来源于stack exchange,提问作者user16357470
相关产品推荐
相关产品推荐

