Flutter JS-Interop中StreamController无法从ListProvider更新值问题
问题分析与解决方案
问题原因
- 引用类型覆盖问题:你在循环中修改的是同一个
Uint8List实例,由于它是引用类型,后续循环的修改会直接覆盖之前实例的内容。当StreamController发送这个实例时,最终所有发送的都是循环结束时的最终值。 - build方法的不当使用:在
build方法中处理发送数据的业务逻辑不合适,build会因多种原因触发重绘,可能导致重复发送或逻辑执行混乱。
解决方案
1. 循环中创建独立的Uint8List实例
每次需要发送时,创建新的Uint8List实例,避免复用同一个引用:
for (int i = 0; i < 8; i++) { final buttonState = ref.read(listProvider1)[i][currentStep]; if (buttonState) { // 创建新的Uint8List实例,基于需求修改值 final newList = Uint8List.fromList([144, 30 + i, 70]); ref.read(listProvider3.notifier).state = newList; sendToJs(newList); // 直接传入新实例 } }
2. 将发送逻辑移出build方法,使用ref.listen监听状态变化
在initState或ConsumerState的build外,通过ref.listen监听steplistProvider的变化,避免在build中执行业务逻辑:
@override void initState() { super.initState(); // 监听步进序号的变化,触发发送逻辑 ref.listen(steplistProvider, (previous, currentStep) { final buttonMatrix = ref.read(listProvider1); for (int i = 0; i < 8; i++) { if (buttonMatrix[i][currentStep]) { final newList = Uint8List.fromList([144, 30 + i, 70]); ref.read(listProvider3.notifier).state = newList; _streamController.add(newList); // 发送独立的新实例 } } }); }
3. 确保StreamController发送的是独立副本
如果必须复用某个基础列表,每次发送前创建副本:
// 发送前创建副本,避免后续修改影响已发送的数据 final sendList = Uint8List.fromList(listBytes); _streamController.add(sendList);
额外建议
- 避免在
build方法中执行状态更新、数据发送这类副作用操作,应该将这类逻辑放到状态监听、事件回调或专门的业务逻辑类中。 - 可以考虑将WebMidi的发送逻辑封装成单独的Provider(比如
StateNotifierProvider),统一管理发送状态和逻辑,提高代码的可维护性。
内容的提问来源于stack exchange,提问作者sensn
相关产品推荐
相关产品推荐

