You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:如何无需无限循环Stream即可用StreamBuilder监听数据变化

替代Timer无限循环的Flutter Stream数据监听方案

现有问题分析

你当前通过Timer.periodic(Duration(seconds: 0))实现无限循环请求数据,这种方式会导致前端疯狂向后端发送请求,不仅浪费资源,还会严重影响性能。下面提供两种更合理的实现方式:


方案一:用Stream.periodic实现可控轮询

直接通过Stream.periodic创建定时轮询的数据流,替代手动维护StreamController和Timer的组合,代码更简洁且易于维护:

1. 定义轮询Stream

Stream<UserModel> getFriendsStream() {
  // 设置合理的轮询间隔,比如5秒,避免频繁请求
  return Stream.periodic(const Duration(seconds: 5), (_) async {
    // 提前获取SharedPreferences,避免每次请求重复初始化
    SharedPreferences prefs = await SharedPreferences.getInstance();
    var token = prefs.getString("token");
    var username = prefs.getString("username");

    try {
      final response = await http.post(
        Uri.parse("http://192.168.0.111:3000/friendNames"),
        headers: {
          "Content-Type": "application/json",
          "authorization": "$token"
        },
        body: jsonEncode({"username": username}),
      );
      return UserModel.fromJson(json.decode(response.body));
    } catch (e) {
      print("请求好友列表失败: $e");
      // 抛出错误,让StreamBuilder可以捕获并处理
      rethrow;
    }
  }).asyncMap((future) => future);
}

2. 初始化Stream并使用StreamBuilder

late Stream<UserModel> _friendsStream;

@override
void initState() {
  super.initState();
  _friendsStream = getFriendsStream();
}

// StreamBuilder部分修改为使用_friendsStream
StreamBuilder<UserModel>(
  stream: _friendsStream,
  builder: (context, snapshot) {
    // 新增错误状态处理
    if (snapshot.hasError) {
      return const Center(child: Text("加载好友列表失败"));
    }
    
    if (snapshot.hasData) {
      final userModel = snapshot.data!;
      if (userModel.msg == "no friends to chat with") {
        return const Center(child: Text("No friends found."));
      } else {
        return ListView.builder(
          itemCount: userModel.msg.length,
          itemBuilder: (context, index) {
            return ListTile(
              title: Text(userModel.msg[index]),
              subtitle: Text("${userModel.msg1![index]}"),
              leading: CircleAvatar(
                backgroundColor: Colors.orange,
                backgroundImage: NetworkImage(
                  "http://192.168.0.111:3000/getImage/${userModel.msg[index]}?v=${Random().nextInt(100)}",
                ),
              ),
              onTap: () => Navigator.of(context).push(
                MaterialPageRoute(builder: (context) => ChatRoom(userModel.msg[index])),
              ),
            );
          },
        );
      }
    } else {
      return const Center(child: CircularProgressIndicator());
    }
  },
)

方案二:使用WebSocket实现实时推送(推荐)

如果后端支持WebSocket,可以实现真正的实时监听,后端在好友列表变化时主动推送数据,彻底告别轮询:

1. 初始化WebSocket连接

late WebSocket _webSocket;
late StreamController<UserModel> _friendsController;

@override
void initState() {
  super.initState();
  _friendsController = StreamController.broadcast();
  _connectWebSocket();
}

void _connectWebSocket() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  var token = prefs.getString("token");
  
  // 连接后端WebSocket服务,需后端配合实现
  try {
    _webSocket = await WebSocket.connect(
      "ws://192.168.0.111:3000/friends-ws?token=$token",
    );
    
    // 监听后端推送的消息
    _webSocket.listen((message) {
      final userModel = UserModel.fromJson(json.decode(message));
      _friendsController.sink.add(userModel);
    }, onError: (error) {
      print("WebSocket连接错误: $error");
      // 可选:添加重连逻辑
    }, onDone: () {
      print("WebSocket连接关闭");
      // 可选:添加重连逻辑
    });
  } catch (e) {
    print("WebSocket连接失败: $e");
  }
}

// 记得在页面销毁时释放资源
@override
void dispose() {
  _webSocket.close();
  _friendsController.close();
  super.dispose();
}

2. StreamBuilder使用WebSocket数据流

StreamBuilder<UserModel>(
  stream: _friendsController.stream,
  builder: (context, snapshot) {
    // 复用之前的builder逻辑,包含错误、空数据、正常数据的处理
    // ...
  },
)

额外优化建议

  • 避免在每次请求中重复获取SharedPreferences,可以在initState中提前获取并保存token和username
  • 处理网络请求的超时情况,避免请求长时间阻塞
  • 对于NetworkImage的随机参数,可以考虑用时间戳代替Random,避免不必要的重复加载

内容的提问来源于stack exchange,提问作者alaa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 13:36:30