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

Signal R事件数据如何正确获取并同步至Flutter UI?

解决方案:Flutter SignalR实时数据更新UI问题

核心问题说明

FutureBuilder 是为一次性异步任务设计的(比如单次接口请求),而SignalR的Animals事件是持续推送的实时数据流,因此用FutureBuilder无法实现UI的实时更新。需要改用状态管理+响应式UI组件来处理。


步骤1:定义数据模型

先把SignalR推送的JSON数据转换成Dart对象,方便后续处理:

class Animal {
  final List<double> warpedBox;
  final String name;
  final DateTime date;
  final String additionInfo;
  final String baseName;
  final String imageGuid;

  Animal({
    required this.warpedBox,
    required this.name,
    required this.date,
    required this.additionInfo,
    required this.baseName,
    required this.imageGuid,
  });

  // 从JSON解析为Dart对象
  factory Animal.fromJson(Map<String, dynamic> json) {
    return Animal(
      warpedBox: (json['warpedBox'] as List).map((e) => e as double).toList(),
      name: json['name'],
      date: DateTime.parse(json['date']),
      additionInfo: json['additionInfo'],
      baseName: json['baseName'],
      imageGuid: json['imageGuid'],
    );
  }
}

步骤2:用StatefulWidget管理SignalR连接与实时数据

在State类中维护动物列表,收到SignalR事件时通过setState更新UI:

class AnimalListScreen extends StatefulWidget {
  const AnimalListScreen({super.key});

  @override
  State<AnimalListScreen> createState() => _AnimalListScreenState();
}

class _AnimalListScreenState extends State<AnimalListScreen> {
  late HubConnection _hubConnection;
  final List<Animal> _animalList = [];

  @override
  void initState() {
    super.initState();
    _initSignalRConnection();
  }

  // 初始化SignalR连接并订阅事件
  Future<void> _initSignalRConnection() async {
    _hubConnection = HubConnectionBuilder()
        .withUrl('你的SignalR Hub地址') // 替换为实际的Hub地址
        .build();

    // 订阅Animals事件,处理推送的数据
    _hubConnection.on('Animals', (arguments) {
      if (arguments?.isNotEmpty == true) {
        // 解析推送的数组数据
        final List<dynamic> rawData = arguments![0] as List;
        final List<Animal> newAnimals = rawData.map((json) => Animal.fromJson(json)).toList();
        
        // 更新状态,触发UI刷新
        setState(() {
          _animalList.addAll(newAnimals);
          // 若需要去重或替换列表,可替换为 _animalList = newAnimals;
        });
      }
    });

    // 启动连接
    try {
      await _hubConnection.start();
      print('SignalR连接成功');
    } catch (e) {
      print('SignalR连接失败: $e');
    }
  }

  @override
  void dispose() {
    // 页面销毁时断开SignalR连接
    _hubConnection.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('动物监测列表')),
      body: _animalList.isEmpty
          ? const Center(child: Text('暂无动物数据'))
          : ListView.builder(
              itemCount: _animalList.length,
              itemBuilder: (context, index) {
                final animal = _animalList[index];
                return ListTile(
                  title: Text(animal.name),
                  subtitle: Text(animal.additionInfo),
                  trailing: Text(animal.date.toLocal().toString().substring(0, 19)),
                );
              },
            ),
    );
  }
}

可选:用StreamBuilder优化数据流管理

如果需要更灵活的数据流处理(比如多个组件共享数据),可以改用StreamController配合StreamBuilder:

class _AnimalListScreenState extends State<AnimalListScreen> {
  late HubConnection _hubConnection;
  final StreamController<List<Animal>> _animalStream = StreamController.broadcast();
  final List<Animal> _animalList = [];

  @override
  void initState() {
    super.initState();
    _initSignalRConnection();
  }

  Future<void> _initSignalRConnection() async {
    _hubConnection = HubConnectionBuilder()
        .withUrl('你的SignalR Hub地址')
        .build();

    _hubConnection.on('Animals', (arguments) {
      if (arguments?.isNotEmpty == true) {
        final List<dynamic> rawData = arguments![0] as List;
        _animalList.addAll(rawData.map((json) => Animal.fromJson(json)).toList());
        _animalStream.add([..._animalList]); // 发送更新后的列表到流
      }
    });

    try {
      await _hubConnection.start();
    } catch (e) {
      print('连接失败: $e');
    }
  }

  @override
  void dispose() {
    _hubConnection.dispose();
    _animalStream.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('动物监测列表')),
      body: StreamBuilder<List<Animal>>(
        stream: _animalStream.stream,
        initialData: _animalList,
        builder: (context, snapshot) {
          if (snapshot.data?.isEmpty == true) {
            return const Center(child: Text('暂无动物数据'));
          }
          return ListView.builder(
            itemCount: snapshot.data!.length,
            itemBuilder: (context, index) {
              final animal = snapshot.data![index];
              return ListTile(
                title: Text(animal.name),
                subtitle: Text(animal.additionInfo),
                trailing: Text(animal.date.toLocal().toString().substring(0, 19)),
              );
            },
          );
        },
      ),
    );
  }
}

关键注意事项

  1. 确保SignalR Hub地址正确,且后端配置了跨域(Web端需要)。
  2. JSON字段名要和后端完全匹配(比如示例中的warpedBox是驼峰命名,Dart模型中需对应)。
  3. 可添加SignalR重连逻辑,避免连接断开后无法接收数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 21:50:12