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

Flutter中如何让Future响应像Stream一样实时更新UI?

让Flutter中API请求的Future实现类似Stream的实时UI同步

下面是几种实用的实现方案,可根据后端能力和业务需求选择:

1. 手动用StreamController包装API请求

把单次的Future请求转换成可主动触发更新的Stream,每次后端数据可能变化时,重新调用API并将结果推入Stream,UI通过StreamBuilder监听更新。

class DataRepository {
  final _controller = StreamController<TransactionState>.broadcast();

  Stream<TransactionState> get transactionStateStream => _controller.stream;

  // 触发API请求并更新Stream
  Future<void> fetchTransactionStatus(String transactionId) async {
    try {
      final response = await http.get(Uri.parse('/api/transactions/$transactionId'));
      final state = TransactionState.fromJson(jsonDecode(response.body));
      _controller.add(state);
    } catch (e) {
      _controller.addError(e);
    }
  }

  // 页面销毁时关闭控制器,避免内存泄漏
  void dispose() {
    _controller.close();
  }
}

// UI层使用StreamBuilder监听
StreamBuilder<TransactionState>(
  stream: repository.transactionStateStream,
  initialData: TransactionState.pending(),
  builder: (context, snapshot) {
    if (snapshot.hasError) {
      return Text('加载失败: ${snapshot.error}');
    }
    switch (snapshot.data?.status) {
      case TransactionStatus.pending:
        return const CircularProgressIndicator();
      case TransactionStatus.success:
        return const Text('交易成功');
      case TransactionStatus.failed:
        return const Text('交易失败');
      default:
        return const Text('未知状态');
    }
  },
)

2. 定期轮询(后端无推送能力时用)

如果后端不支持实时推送,用Timer定期调用API刷新数据,适合交易状态这类不需要毫秒级实时的场景。

class TransactionPage extends StatefulWidget {
  final String transactionId;

  const TransactionPage(this.transactionId, {super.key});

  @override
  State<TransactionPage> createState() => _TransactionPageState();
}

class _TransactionPageState extends State<TransactionPage> {
  late Timer _pollingTimer;
  final _repository = DataRepository();

  @override
  void initState() {
    super.initState();
    // 初始化时先请求一次,然后每3秒轮询一次
    _repository.fetchTransactionStatus(widget.transactionId);
    _pollingTimer = Timer.periodic(const Duration(seconds: 3), (_) {
      _repository.fetchTransactionStatus(widget.transactionId);
    });
  }

  @override
  void dispose() {
    _pollingTimer.cancel();
    _repository.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<TransactionState>(
      stream: _repository.transactionStateStream,
      // ... 同上面的builder逻辑
    );
  }
}

3. 结合状态管理工具(以Riverpod为例)

用状态管理工具统一管理数据状态,主动触发刷新时更新UI,比手动管理Stream更简洁。

// 定义StreamProvider
final transactionStateProvider = StreamProvider.family<TransactionState, String>((ref, transactionId) {
  final controller = StreamController<TransactionState>.broadcast();
  
  // 定义刷新方法
  Future<void> fetchData() async {
    final response = await http.get(Uri.parse('/api/transactions/$transactionId'));
    final state = TransactionState.fromJson(jsonDecode(response.body));
    controller.add(state);
  }

  // 初始加载 + 监听外部刷新信号
  fetchData();
  ref.onDispose(() => controller.close());
  
  // 暴露刷新能力给UI调用
  ref.listenSelf((previous, next) {
    if (next.isRefreshing) fetchData();
  });

  return controller.stream;
});

// UI层使用
Consumer(
  builder: (context, ref, child) {
    final transactionState = ref.watch(transactionStateProvider('tx_123'));
    return transactionState.when(
      loading: () => const CircularProgressIndicator(),
      error: (e, _) => Text('加载失败: $e'),
      data: (state) {
        switch (state.status) {
          case TransactionStatus.pending:
            return const Text('待处理');
          case TransactionStatus.success:
            return const Text('交易成功');
          default:
            return const Text('交易失败');
        }
      },
    );
  },
)

// 主动触发刷新(比如用户下拉刷新时)
ref.invalidate(transactionStateProvider('tx_123'));

4. 后端支持WebSocket/SSE时用实时推送

如果后端能通过WebSocket或Server-Sent Events主动推送数据变化,直接建立长连接接收更新,这是最高效的方式。

class WebSocketManager {
  WebSocket? _socket;
  final _controller = StreamController<TransactionState>.broadcast();

  Stream<TransactionState> get transactionUpdates => _controller.stream;

  Future<void> connect(String transactionId) async {
    _socket = await WebSocket.connect('wss://your-server.com/ws/transactions/$transactionId');
    _socket?.listen((data) {
      final state = TransactionState.fromJson(jsonDecode(data));
      _controller.add(state);
    });
  }

  void disconnect() {
    _socket?.close();
    _controller.close();
  }
}

// UI层使用
StreamBuilder<TransactionState>(
  stream: webSocketManager.transactionUpdates,
  initialData: TransactionState.pending(),
  builder: (context, snapshot) {
    // 渲染UI逻辑
  },
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 22:26:25